From 261cdc5690f8f1da60426c4d2c98f40445d71545 Mon Sep 17 00:00:00 2001 From: Jochen Sprickerhof Date: Sat, 10 Nov 2018 10:44:09 +0100 Subject: [PATCH] New upstream version 1.9.0+dfsg1 --- .appveyor.yml | 99 + .github/change_log.py | 448 ++++ .github/generate_abi_reports.sh | 144 ++ .github/issue_template.md | 17 +- .github/stale.yml | 28 + .travis.sh | 303 +-- .travis.yml | 59 +- 2d/CMakeLists.txt | 5 +- 2d/include/pcl/2d/edge.h | 4 +- 2d/include/pcl/2d/impl/edge.hpp | 4 +- 2d/include/pcl/2d/impl/morphology.hpp | 10 +- 2d/src/examples.cpp | 8 +- CHANGES.md | 497 ++++- CMakeLists.txt | 170 +- PCLConfig.cmake.in | 524 ++--- README.md | 13 +- .../feature_wrapper/normal_estimator.h | 2 +- .../pipeline/global_nn_recognizer_crh.h | 5 +- .../pipeline/global_nn_recognizer_cvfh.h | 5 +- .../impl/global_nn_recognizer_crh.hpp | 5 +- .../impl/global_nn_recognizer_cvfh.hpp | 7 +- .../pipeline/impl/local_recognizer.hpp | 5 +- .../pipeline/local_recognizer.h | 7 +- .../pcl/apps/3d_rec_framework/utils/metrics.h | 2 +- .../src/local_recognition_mian_dataset.cpp | 2 +- apps/CMakeLists.txt | 6 +- .../pcl/apps/cloud_composer/item_inspector.h | 2 +- .../tools/impl/organized_segmentation.hpp | 20 +- apps/cloud_composer/src/commands.cpp | 2 +- apps/cloud_composer/src/items/cloud_item.cpp | 2 +- apps/cloud_composer/src/items/fpfh_item.cpp | 2 +- apps/cloud_composer/src/merge_selection.cpp | 2 +- .../src/point_selectors/selection_event.cpp | 2 +- apps/cloud_composer/src/project_model.cpp | 4 +- apps/cloud_composer/src/properties_model.cpp | 1 - apps/cloud_composer/src/transform_clouds.cpp | 2 +- .../tools/organized_segmentation.cpp | 2 +- apps/cloud_composer/tools/supervoxels.cpp | 2 +- apps/in_hand_scanner/src/icp.cpp | 2 +- apps/in_hand_scanner/src/mesh_processing.cpp | 2 +- .../pcl/apps/dominant_plane_segmentation.h | 2 +- .../pcl/apps/organized_segmentation_demo.h | 2 +- apps/include/pcl/apps/pcd_video_player.h | 2 +- .../pcl/apps/render_views_tesselated_sphere.h | 12 +- apps/modeler/src/parameter_dialog.cpp | 2 +- .../pcl/apps/optronic_viewer/filter_window.h | 2 +- .../pcl/apps/point_cloud_editor/cloud.h | 10 +- .../point_cloud_editor/cloudEditorWidget.h | 2 +- .../apps/point_cloud_editor/commandQueue.h | 6 +- .../point_cloud_editor/transformCommand.h | 2 +- apps/point_cloud_editor/src/cloud.cpp | 4 +- .../src/cloudEditorWidget.cpp | 5 + .../src/cloudTransformTool.cpp | 2 - apps/point_cloud_editor/src/common.cpp | 2 +- .../src/selectionTransformTool.cpp | 6 +- apps/src/ni_linemod.cpp | 6 +- apps/src/openni_mls_smoothing.cpp | 19 +- apps/src/organized_segmentation_demo.cpp | 13 +- apps/src/pcd_select_object_plane.cpp | 5 +- .../src/pcd_video_player/pcd_video_player.cpp | 4 +- apps/src/render_views_tesselated_sphere.cpp | 2 +- apps/src/stereo_ground_segmentation.cpp | 16 +- cmake/CMakeParseArguments.cmake | 2 +- cmake/Modules/FindEnsenso.cmake | 61 +- cmake/Modules/FindFLANN.cmake | 52 +- cmake/Modules/FindOpenNI.cmake | 65 +- cmake/Modules/FindOpenNI2.cmake | 73 +- cmake/Modules/FindQhull.cmake | 41 +- cmake/Modules/NSIS.template.in | 4 +- cmake/pcl_cpack.cmake | 4 +- cmake/pcl_find_boost.cmake | 5 +- cmake/pcl_find_cuda.cmake | 111 +- cmake/pcl_find_sse.cmake | 49 +- cmake/pcl_options.cmake | 4 + cmake/pcl_pclconfig.cmake | 22 +- cmake/pcl_targets.cmake | 18 +- cmake/pcl_utils.cmake | 2 +- common/CMakeLists.txt | 9 +- .../include/pcl/common/bivariate_polynomial.h | 4 +- common/include/pcl/common/centroid.h | 84 +- common/include/pcl/common/colors.h | 26 +- common/include/pcl/common/common.h | 2 +- common/include/pcl/common/eigen.h | 4 +- common/include/pcl/common/fft/kiss_fft.h | 4 - common/include/pcl/common/generate.h | 12 +- common/include/pcl/common/geometry.h | 57 + .../pcl/common/impl/bivariate_polynomial.hpp | 2 +- common/include/pcl/common/impl/common.hpp | 14 + common/include/pcl/common/impl/eigen.hpp | 10 +- .../include/pcl/common/impl/intersections.hpp | 2 +- common/include/pcl/common/impl/pca.hpp | 20 +- .../common/impl/polynomial_calculations.hpp | 89 +- common/include/pcl/common/impl/transforms.hpp | 270 ++- common/include/pcl/common/intensity.h | 6 +- common/include/pcl/common/intersections.h | 2 +- common/include/pcl/common/io.h | 19 +- common/include/pcl/common/pca.h | 8 - .../include/pcl/common/poses_from_matches.h | 2 +- common/include/pcl/common/spring.h | 4 +- common/include/pcl/common/time.h | 2 +- common/include/pcl/common/time_trigger.h | 2 +- common/include/pcl/common/utils.h | 2 +- common/include/pcl/common/vector_average.h | 5 +- common/include/pcl/console/parse.h | 74 +- common/include/pcl/correspondence.h | 6 +- common/include/pcl/exceptions.h | 4 +- common/include/pcl/impl/instantiate.hpp | 1 - common/include/pcl/impl/point_types.hpp | 61 +- common/include/pcl/pcl_base.h | 40 +- common/include/pcl/pcl_exports.h | 2 +- common/include/pcl/pcl_macros.h | 80 +- common/include/pcl/pcl_tests.h | 51 +- common/include/pcl/point_cloud.h | 120 +- common/include/pcl/point_representation.h | 20 +- common/include/pcl/point_traits.h | 9 +- common/include/pcl/point_types.h | 27 + common/include/pcl/point_types_conversion.h | 24 +- common/include/pcl/range_image/range_image.h | 8 +- .../pcl/range_image/range_image_planar.h | 4 +- .../pcl/range_image/range_image_spherical.h | 2 +- common/include/pcl/ros/conversions.h | 125 -- common/src/colors.cpp | 298 ++- common/src/fft/kiss_fft.c | 2 +- common/src/parse.cpp | 95 +- common/src/point_types.cpp | 31 + common/src/poses_from_matches.cpp | 4 +- cuda/common/include/pcl/cuda/common/eigen.h | 2 +- .../pcl/cuda/features/normal_3d_kernels.h | 6 +- cuda/io/src/extract_indices.cu | 2 +- cuda/nn/organized_neighbor_search.hpp | 2 +- .../src/sac_model_1point_plane.cu | 8 +- cuda/sample_consensus/src/sac_model_plane.cu | 8 +- cuda/segmentation/src/mssegmentation.cpp | 4 +- doc/advanced/content/conf.py | 4 +- doc/advanced/content/index.rst | 2 +- doc/advanced/content/pcl2.rst | 4 +- doc/doxygen/CMakeLists.txt | 2 +- doc/doxygen/doxyfile.in | 4 +- doc/tutorials/content/adding_custom_ptype.rst | 12 +- doc/tutorials/content/basic_structures.rst | 4 +- doc/tutorials/content/benchmark.rst | 4 +- doc/tutorials/content/benchmark_filters.rst | 2 +- doc/tutorials/content/building_pcl.rst | 10 +- .../compiling_pcl_dependencies_windows.rst | 10 +- doc/tutorials/content/compiling_pcl_posix.rst | 2 +- .../content/compiling_pcl_windows.rst | 12 +- doc/tutorials/content/compression.rst | 20 +- doc/tutorials/content/concatenate_clouds.rst | 4 +- doc/tutorials/content/concatenate_points.rst | 2 +- .../conditional_euclidean_clustering.rst | 4 +- doc/tutorials/content/conditional_removal.rst | 6 +- doc/tutorials/content/depth_sense_grabber.rst | 2 +- doc/tutorials/content/don_segmentation.rst | 8 +- doc/tutorials/content/ensenso_cameras.rst | 2 +- doc/tutorials/content/extract_indices.rst | 2 +- doc/tutorials/content/gasd_estimation.rst | 153 ++ doc/tutorials/content/gpu_install.rst | 2 +- doc/tutorials/content/gpu_people.rst | 2 +- .../ground_based_rgbd_people_detection.rst | 2 +- doc/tutorials/content/hdl_grabber.rst | 43 +- doc/tutorials/content/how_features_work.rst | 6 +- doc/tutorials/content/hull_2d.rst | 8 +- .../content/images/gasd_estimation.png | Bin 0 -> 68235 bytes .../content/images/gasd_estimation/grid.png | Bin 0 -> 68235 bytes .../grid_top_side_bottom_view.png | Bin 0 -> 133488 bytes doc/tutorials/content/index.rst | 15 + doc/tutorials/content/interactive_icp.rst | 8 +- .../content/iterative_closest_point.rst | 2 +- doc/tutorials/content/matrix_transform.rst | 6 +- .../content/min_cut_segmentation.rst | 2 +- .../content/narf_feature_extraction.rst | 4 +- doc/tutorials/content/normal_estimation.rst | 2 +- doc/tutorials/content/octree.rst | 2 +- doc/tutorials/content/openni_grabber.rst | 4 +- doc/tutorials/content/pcd_file_format.rst | 2 +- doc/tutorials/content/pcl_plotter.rst | 2 +- doc/tutorials/content/pcl_visualizer.rst | 2 +- doc/tutorials/content/project_inliers.rst | 2 +- doc/tutorials/content/qt_visualizer.rst | 8 +- .../content/random_sample_consensus.rst | 4 +- .../content/range_image_creation.rst | 2 +- .../region_growing_rgb_segmentation.rst | 2 +- .../content/region_growing_segmentation.rst | 2 +- doc/tutorials/content/registration_api.rst | 2 +- doc/tutorials/content/remove_outliers.rst | 4 +- doc/tutorials/content/rops_feature.rst | 2 +- .../conditional_euclidean_clustering.cpp | 4 +- .../don_segmentation/don_segmentation.cpp | 2 +- .../global_hypothesis_verification.cpp | 5 +- .../sources/iccv2011/include/registration.h | 2 +- .../sources/iccv2011/include/segmentation.h | 2 +- .../iccv2011/src/test_registration.cpp | 2 +- .../interactive_icp/interactive_icp.cpp | 2 +- .../sources/iros2011/include/registration.h | 2 +- .../iros2011/include/solution/registration.h | 2 +- .../iros2011/include/solution/segmentation.h | 2 +- .../iros2011/src/test_registration.cpp | 2 +- .../iterative_closest_point.cpp | 2 +- .../matrix_transform/matrix_transform.cpp | 2 +- .../narf_feature_extraction.cpp | 2 +- .../narf_keypoint_extraction.cpp | 2 +- .../pcl_visualizer/pcl_visualizer_demo.cpp | 2 +- .../sources/qt_colorize_cloud/CMakeLists.txt | 43 +- .../sources/qt_colorize_cloud/pclviewer.cpp | 2 +- .../sources/qt_visualizer/CMakeLists.txt | 43 +- .../sources/qt_visualizer/pclviewer.cpp | 2 +- .../random_sample_consensus.cpp | 2 +- .../range_image_border_extraction.cpp | 2 +- .../range_image_visualization.cpp | 2 +- .../template_alignment/template_alignment.cpp | 2 +- doc/tutorials/content/statistical_outlier.rst | 2 +- .../content/supervoxel_clustering.rst | 4 +- doc/tutorials/content/template_alignment.rst | 2 +- doc/tutorials/content/tracking.rst | 6 +- .../content/using_pcl_pcl_config.rst | 4 +- doc/tutorials/content/vfh_estimation.rst | 2 +- doc/tutorials/content/vfh_recognition.rst | 2 +- doc/tutorials/content/walkthrough.rst | 146 +- doc/tutorials/content/writing_new_classes.rst | 36 +- doc/tutorials/content/writing_pcd.rst | 19 +- .../example_difference_of_normals.cpp | 2 +- examples/features/example_rift_estimation.cpp | 2 +- .../segmentation/example_cpc_segmentation.cpp | 18 +- .../example_extract_clusters_normals.cpp | 2 +- .../example_lccp_segmentation.cpp | 7 +- examples/segmentation/example_supervoxels.cpp | 5 +- features/CMakeLists.txt | 8 +- features/include/pcl/features/brisk_2d.h | 2 +- features/include/pcl/features/cvfh.h | 4 +- features/include/pcl/features/flare.h | 293 +++ features/include/pcl/features/fpfh_omp.h | 14 +- features/include/pcl/features/from_meshes.h | 3 +- features/include/pcl/features/gasd.h | 366 ++++ features/include/pcl/features/impl/3dsc.hpp | 2 +- features/include/pcl/features/impl/flare.hpp | 263 +++ .../include/pcl/features/impl/fpfh_omp.hpp | 22 +- features/include/pcl/features/impl/gasd.hpp | 402 ++++ .../features/impl/integral_image_normal.hpp | 4 +- .../pcl/features/impl/moment_invariants.hpp | 4 +- .../pcl/features/impl/normal_3d_omp.hpp | 45 +- .../pcl/features/impl/shot_lrf_omp.hpp | 18 +- .../include/pcl/features/impl/shot_omp.hpp | 28 + features/include/pcl/features/impl/usc.hpp | 2 +- features/include/pcl/features/impl/vfh.hpp | 2 +- .../include/pcl/features/moment_invariants.h | 1 - features/include/pcl/features/normal_3d.h | 43 + features/include/pcl/features/normal_3d_omp.h | 10 +- features/include/pcl/features/our_cvfh.h | 4 +- .../include/pcl/features/rops_estimation.h | 14 +- features/include/pcl/features/rsd.h | 2 +- features/include/pcl/features/shot_lrf_omp.h | 10 +- features/include/pcl/features/shot_omp.h | 18 +- features/include/pcl/features/spin_image.h | 2 +- features/include/pcl/features/usc.h | 2 +- features/src/flare.cpp | 49 + .../src/gasd.cpp | 25 +- features/src/moment_of_inertia_estimation.cpp | 7 +- features/src/range_image_border_extractor.cpp | 2 +- filters/include/pcl/filters/clipper3D.h | 1 + .../include/pcl/filters/conditional_removal.h | 21 +- filters/include/pcl/filters/convolution.h | 4 +- filters/include/pcl/filters/convolution_3d.h | 4 +- .../include/pcl/filters/covariance_sampling.h | 2 +- filters/include/pcl/filters/crop_box.h | 1 - .../include/pcl/filters/fast_bilateral_omp.h | 13 +- filters/include/pcl/filters/filter.h | 34 +- filters/include/pcl/filters/filter_indices.h | 8 +- .../include/pcl/filters/impl/bilateral.hpp | 2 +- .../pcl/filters/impl/box_clipper3D.hpp | 26 +- .../pcl/filters/impl/conditional_removal.hpp | 14 +- .../pcl/filters/impl/covariance_sampling.hpp | 65 +- .../pcl/filters/impl/extract_indices.hpp | 4 +- .../pcl/filters/impl/fast_bilateral_omp.hpp | 14 + filters/include/pcl/filters/impl/filter.hpp | 16 +- .../pcl/filters/impl/frustum_culling.hpp | 2 +- .../pcl/filters/impl/project_inliers.hpp | 2 +- .../pcl/filters/impl/random_sample.hpp | 53 +- .../pcl/filters/impl/uniform_sampling.hpp | 16 + .../include/pcl/filters/impl/voxel_grid.hpp | 2 +- .../pcl/filters/model_outlier_removal.h | 2 +- filters/include/pcl/filters/passthrough.h | 34 +- filters/include/pcl/filters/random_sample.h | 1 - .../pcl/filters/sampling_surface_normal.h | 2 +- .../pcl/filters/statistical_outlier_removal.h | 47 +- .../include/pcl/filters/uniform_sampling.h | 3 +- filters/include/pcl/filters/voxel_grid.h | 283 +-- .../pcl/filters/voxel_grid_covariance.h | 4 +- .../filters/voxel_grid_occlusion_estimation.h | 2 +- filters/src/extract_indices.cpp | 2 +- filters/src/passthrough.cpp | 2 +- filters/src/statistical_outlier_removal.cpp | 189 +- geometry/include/pcl/geometry/mesh_traits.h | 2 +- .../include/pcl/gpu/containers/device_array.h | 12 +- .../pcl/gpu/containers/device_memory.h | 6 +- .../pcl/gpu/containers/initialization.h | 12 +- gpu/containers/src/initialization.cpp | 4 +- gpu/features/CMakeLists.txt | 3 +- .../include/pcl/gpu/features/device/eigen.hpp | 2 +- gpu/features/src/spinimages.cu | 10 +- gpu/features/src/vfh.cu | 4 +- gpu/features/test/data_source.hpp | 6 +- .../include/pcl/gpu/kinfu/color_volume.h | 2 +- gpu/kinfu/include/pcl/gpu/kinfu/kinfu.h | 4 +- .../include/pcl/gpu/kinfu/marching_cubes.h | 8 +- gpu/kinfu/include/pcl/gpu/kinfu/raycaster.h | 2 +- gpu/kinfu/include/pcl/gpu/kinfu/tsdf_volume.h | 4 +- gpu/kinfu/src/cuda/extract.cu | 13 +- gpu/kinfu/src/cuda/maps.cu | 2 +- gpu/kinfu/src/cuda/marching_cubes.cu | 14 +- gpu/kinfu/src/cuda/tsdf_volume.cu | 2 +- gpu/kinfu/src/cuda/utils.hpp | 15 +- gpu/kinfu/src/internal.h | 24 +- gpu/kinfu/src/kinfu.cpp | 6 +- gpu/kinfu/tools/kinfu_app_sim.cpp | 6 +- gpu/kinfu/tools/record_tsdfvolume.cpp | 2 +- gpu/kinfu/tools/tsdf_volume.h | 12 +- gpu/kinfu/tools/tsdf_volume.hpp | 6 +- .../pcl/gpu/kinfu_large_scale/color_volume.h | 2 +- .../gpu/kinfu_large_scale/cyclical_buffer.h | 2 +- .../kinfu_large_scale/impl/world_model.hpp | 2 +- .../include/pcl/gpu/kinfu_large_scale/kinfu.h | 4 +- .../gpu/kinfu_large_scale/marching_cubes.h | 6 +- .../pcl/gpu/kinfu_large_scale/raycaster.h | 2 +- .../pcl/gpu/kinfu_large_scale/tsdf_volume.h | 12 +- .../pcl/gpu/kinfu_large_scale/world_model.h | 6 +- gpu/kinfu_large_scale/src/cuda/extract.cu | 23 +- gpu/kinfu_large_scale/src/cuda/maps.cu | 2 +- .../src/cuda/marching_cubes.cu | 14 + gpu/kinfu_large_scale/src/cuda/tsdf_volume.cu | 6 +- gpu/kinfu_large_scale/src/cuda/utils.hpp | 18 +- gpu/kinfu_large_scale/src/internal.h | 30 +- gpu/kinfu_large_scale/src/kinfu.cpp | 8 +- gpu/kinfu_large_scale/src/tsdf_volume.cpp | 4 +- gpu/kinfu_large_scale/tools/kinfu_app_sim.cpp | 6 +- .../tools/record_maps_rgb.cpp | 2 +- .../tools/record_tsdfvolume.cpp | 2 +- .../tools/standalone_texture_mapping.cpp | 8 +- gpu/octree/include/pcl/gpu/octree/octree.hpp | 16 +- gpu/octree/src/cuda/octree_builder.cu | 2 +- gpu/octree/src/cuda/octree_host.cu | 8 +- gpu/octree/src/cuda/octree_iterator.hpp | 12 +- .../src/utils/priority_octree_iterator.hpp | 12 +- gpu/octree/test/perfomance.cpp | 8 +- gpu/octree/test/test_host_radius_search.cpp | 2 +- gpu/people/CMakeLists.txt | 13 +- .../include/pcl/gpu/people/label_blob2.h | 2 +- .../include/pcl/gpu/people/label_common.h | 4 +- .../include/pcl/gpu/people/label_segment.h | 2 +- .../include/pcl/gpu/people/label_tree.h | 6 +- .../include/pcl/gpu/people/person_attribs.h | 2 +- gpu/people/src/cuda/smooth.cu | 2 +- gpu/people/tools/people_app.cpp | 2 +- gpu/people/tools/people_pcd_prob.cpp | 2 +- gpu/surface/src/cuda/convex_hull.cu | 21 +- gpu/surface/src/internal.h | 2 +- gpu/utils/CMakeLists.txt | 3 +- .../include/pcl/gpu/utils/device/block.hpp | 4 +- .../include/pcl/gpu/utils/device/reduce.hpp | 4 +- io/CMakeLists.txt | 9 + io/include/pcl/compression/color_coding.h | 8 +- .../impl/organized_pointcloud_compression.hpp | 4 +- .../octree_pointcloud_compression.h | 4 +- .../organized_pointcloud_compression.h | 2 +- .../organized_pointcloud_conversion.h | 69 +- io/include/pcl/compression/point_coding.h | 4 +- io/include/pcl/io/ascii_io.h | 2 +- io/include/pcl/io/boost.h | 8 +- io/include/pcl/io/file_io.h | 8 +- io/include/pcl/io/fotonic_grabber.h | 2 +- io/include/pcl/io/grabber.h | 2 +- io/include/pcl/io/hdl_grabber.h | 90 +- io/include/pcl/io/impl/lzf_image_io.hpp | 8 + io/include/pcl/io/impl/pcd_io.hpp | 158 +- io/include/pcl/io/low_level_io.h | 215 ++ io/include/pcl/io/oni_grabber.h | 2 +- io/include/pcl/io/openni2/openni2_device.h | 6 +- .../pcl/io/openni2/openni2_device_manager.h | 2 +- .../pcl/io/openni_camera/openni_driver.h | 2 +- io/include/pcl/io/pcd_io.h | 117 +- io/include/pcl/io/ply/byte_order.h | 2 +- io/include/pcl/io/ply/ply_parser.h | 5 +- io/include/pcl/io/ply_io.h | 16 +- io/include/pcl/io/png_io.h | 39 +- io/include/pcl/io/real_sense_grabber.h | 2 +- io/include/pcl/io/vlp_grabber.h | 34 +- io/include/pcl/io/vtk_io.h | 2 +- io/src/dinast_grabber.cpp | 10 +- io/src/ensenso_grabber.cpp | 10 +- io/src/hdl_grabber.cpp | 145 +- io/src/ifs_io.cpp | 2 +- io/src/lzf.cpp | 6 +- io/src/lzf_image_io.cpp | 66 +- io/src/obj_io.cpp | 58 +- io/src/openni2/openni2_device.cpp | 10 +- io/src/openni2_grabber.cpp | 2 +- io/src/openni_grabber.cpp | 2 +- io/src/pcd_grabber.cpp | 28 +- io/src/pcd_io.cpp | 1036 ++++----- io/src/ply/ply_parser.cpp | 2 +- io/src/ply_io.cpp | 33 +- io/src/png_io.cpp | 12 - io/src/real_sense_grabber.cpp | 2 +- io/src/vlp_grabber.cpp | 75 +- io/src/vtk_lib_io.cpp | 28 +- io/tools/converter.cpp | 2 +- io/tools/openni_pcd_recorder.cpp | 2 +- io/tools/ply/ply2obj.cpp | 3 +- io/tools/ply/ply2ply.cpp | 4 +- io/tools/ply/ply2raw.cpp | 3 +- .../include/pcl/kdtree/impl/kdtree_flann.hpp | 2 +- kdtree/include/pcl/kdtree/kdtree_flann.h | 12 +- keypoints/CMakeLists.txt | 6 + keypoints/include/pcl/keypoints/agast_2d.h | 4 +- keypoints/include/pcl/keypoints/harris_2d.h | 2 +- .../pcl/keypoints/impl/trajkovic_3d.hpp | 4 +- keypoints/include/pcl/keypoints/susan.h | 2 +- ml/CMakeLists.txt | 40 +- ml/include/pcl/ml/feature_handler.h | 2 +- ml/include/pcl/ml/impl/kmeans.hpp | 2 +- .../ml/regression_variance_stats_estimator.h | 2 +- ml/include/pcl/ml/stats_estimator.h | 4 +- ml/include/pcl/ml/svm_wrapper.h | 2 +- ml/src/kmeans.cpp | 6 +- ml/src/svm.cpp | 2 +- octree/CMakeLists.txt | 2 +- .../pcl/octree/impl/octree2buf_base.hpp | 8 +- .../include/pcl/octree/impl/octree_base.hpp | 2 +- .../pcl/octree/impl/octree_iterator.hpp | 131 +- .../pcl/octree/impl/octree_pointcloud.hpp | 24 +- .../impl/octree_pointcloud_adjacency.hpp | 6 +- .../include/pcl/octree/impl/octree_search.hpp | 2 +- octree/include/pcl/octree/octree2buf_base.h | 57 +- octree/include/pcl/octree/octree_base.h | 159 +- octree/include/pcl/octree/octree_iterator.h | 300 ++- octree/include/pcl/octree/octree_key.h | 11 +- octree/include/pcl/octree/octree_pointcloud.h | 22 - .../pcl/octree/octree_pointcloud_adjacency.h | 20 - octree/include/pcl/octree/octree_search.h | 1 + octree/octree.doxy | 2 +- outofcore/include/pcl/outofcore/cJSON.h | 2 +- .../pcl/outofcore/impl/octree_base_node.hpp | 3 +- .../outofcore/impl/octree_disk_container.hpp | 4 +- .../include/pcl/outofcore/octree_base_node.h | 2 +- .../pcl/outofcore/octree_disk_container.h | 4 +- .../pcl/outofcore/octree_ram_container.h | 2 +- outofcore/src/outofcore_base_data.cpp | 3 + outofcore/tools/outofcore_print.cpp | 2 +- outofcore/tools/outofcore_process.cpp | 2 +- pcl.png | Bin 0 -> 14345 bytes .../ground_based_people_detection_app.h | 2 +- .../pcl/people/impl/head_based_subcluster.hpp | 2 +- people/src/hog.cpp | 3 +- .../pcl/recognition/color_gradient_modality.h | 6 +- .../include/pcl/recognition/crh_alignment.h | 2 +- .../recognition/face_detection/face_common.h | 4 + .../include/pcl/recognition/hv/hv_go.h | 7 +- .../impl/cg/geometric_consistency.hpp | 8 +- .../pcl/recognition/impl/cg/hough_3d.hpp | 2 +- .../impl/hv/greedy_verification.hpp | 2 +- .../pcl/recognition/impl/hv/hv_papazov.hpp | 4 +- .../recognition/impl/linemod/line_rgbd.hpp | 2 +- .../pcl/recognition/implicit_shape_model.h | 8 +- .../pcl/recognition/linemod/line_rgbd.h | 4 +- .../pcl/recognition/quantizable_modality.h | 2 +- .../pcl/recognition/ransac_based/auxiliary.h | 6 +- .../ransac_based/voxel_structure.h | 2 +- .../pcl/recognition/surface_normal_modality.h | 4 +- .../face_detector_data_provider.cpp | 2 +- registration/CMakeLists.txt | 1 - .../registration/correspondence_estimation.h | 18 +- ...correspondence_estimation_backprojection.h | 2 +- ...orrespondence_estimation_normal_shooting.h | 2 +- .../registration/correspondence_rejection.h | 17 +- ...orrespondence_rejection_sample_consensus.h | 33 - .../pcl/registration/correspondence_sorting.h | 25 +- .../pcl/registration/correspondence_types.h | 2 +- .../pcl/registration/edge_measurements.h | 1 + .../include/pcl/registration/exceptions.h | 2 +- registration/include/pcl/registration/gicp.h | 13 +- .../include/pcl/registration/graph_handler.h | 2 +- .../include/pcl/registration/ia_fpcs.h | 2 +- registration/include/pcl/registration/icp.h | 1 + .../impl/correspondence_estimation.hpp | 14 - ...respondence_rejection_sample_consensus.hpp | 38 - .../impl/default_convergence_criteria.hpp | 9 +- .../include/pcl/registration/impl/gicp.hpp | 14 +- .../include/pcl/registration/impl/ia_fpcs.hpp | 20 +- .../pcl/registration/impl/ia_kfpcs.hpp | 6 +- .../pcl/registration/impl/ia_ransac.hpp | 2 +- .../include/pcl/registration/impl/icp.hpp | 7 +- .../pcl/registration/impl/joint_icp.hpp | 2 +- .../include/pcl/registration/impl/ndt.hpp | 24 +- .../include/pcl/registration/impl/ndt_2d.hpp | 14 +- .../pcl/registration/impl/registration.hpp | 15 - registration/include/pcl/registration/ndt.h | 17 +- .../include/pcl/registration/ndt_2d.h | 3 +- .../registration/pyramid_feature_matching.h | 2 +- .../include/pcl/registration/registration.h | 49 +- .../registration/transformation_estimation.h | 2 +- .../transformation_estimation_2D.h | 2 +- .../transformation_estimation_3point.h | 2 +- .../transformation_estimation_dq.h | 2 +- ...ransformation_estimation_dual_quaternion.h | 2 +- .../transformation_estimation_lm.h | 6 +- ...sformation_estimation_point_to_plane_lls.h | 4 +- ...n_estimation_point_to_plane_lls_weighted.h | 4 +- ...ation_estimation_point_to_plane_weighted.h | 6 +- .../transformation_estimation_svd.h | 2 +- .../registration/transformation_validation.h | 2 +- registration/src/gicp6d.cpp | 2 +- .../pcl/sample_consensus/impl/lmeds.hpp | 2 +- .../pcl/sample_consensus/impl/mlesac.hpp | 2 +- .../pcl/sample_consensus/impl/msac.hpp | 2 +- .../pcl/sample_consensus/impl/ransac.hpp | 2 +- .../pcl/sample_consensus/impl/rmsac.hpp | 2 +- .../pcl/sample_consensus/impl/rransac.hpp | 2 +- .../impl/sac_model_circle.hpp | 18 +- .../impl/sac_model_circle3d.hpp | 26 +- .../sample_consensus/impl/sac_model_cone.hpp | 28 +- .../impl/sac_model_cylinder.hpp | 30 +- .../sample_consensus/impl/sac_model_line.hpp | 14 +- .../impl/sac_model_normal_parallel_plane.hpp | 2 +- .../impl/sac_model_normal_plane.hpp | 4 +- .../impl/sac_model_normal_sphere.hpp | 6 +- .../impl/sac_model_parallel_line.hpp | 6 +- .../impl/sac_model_parallel_plane.hpp | 6 +- .../impl/sac_model_perpendicular_plane.hpp | 6 +- .../sample_consensus/impl/sac_model_plane.hpp | 12 +- .../impl/sac_model_registration.hpp | 20 +- .../impl/sac_model_registration_2d.hpp | 4 +- .../impl/sac_model_sphere.hpp | 16 +- .../sample_consensus/impl/sac_model_stick.hpp | 12 +- .../pcl/sample_consensus/model_types.h | 33 - .../include/pcl/sample_consensus/sac.h | 2 +- .../include/pcl/sample_consensus/sac_model.h | 54 +- .../pcl/sample_consensus/sac_model_circle.h | 69 +- .../pcl/sample_consensus/sac_model_circle3d.h | 36 +- .../pcl/sample_consensus/sac_model_cone.h | 79 +- .../pcl/sample_consensus/sac_model_cylinder.h | 94 +- .../pcl/sample_consensus/sac_model_line.h | 42 +- .../sac_model_normal_parallel_plane.h | 2 +- .../sample_consensus/sac_model_normal_plane.h | 10 +- .../sac_model_normal_sphere.h | 12 +- .../sac_model_parallel_line.h | 6 +- .../sac_model_parallel_plane.h | 6 +- .../sac_model_perpendicular_plane.h | 12 +- .../pcl/sample_consensus/sac_model_plane.h | 42 +- .../sample_consensus/sac_model_registration.h | 14 +- .../sac_model_registration_2d.h | 4 +- .../pcl/sample_consensus/sac_model_sphere.h | 79 +- .../pcl/sample_consensus/sac_model_stick.h | 42 +- search/include/pcl/search/flann_search.h | 2 +- search/include/pcl/search/impl/organized.hpp | 2 +- search/include/pcl/search/organized.h | 5 +- .../include/pcl/segmentation/comparator.h | 2 + .../conditional_euclidean_clustering.h | 17 + .../pcl/segmentation/cpc_segmentation.h | 2 +- .../euclidean_cluster_comparator.h | 307 +-- .../pcl/segmentation/grabcut_segmentation.h | 11 +- .../segmentation/ground_plane_comparator.h | 2 +- .../segmentation/impl/cpc_segmentation.hpp | 12 +- .../segmentation/impl/lccp_segmentation.hpp | 8 +- ...nized_connected_component_segmentation.hpp | 2 +- .../organized_multi_plane_segmentation.hpp | 2 +- .../segmentation/impl/region_growing_rgb.hpp | 4 +- .../segmentation/impl/segment_differences.hpp | 31 +- .../pcl/segmentation/lccp_segmentation.h | 6 +- ...ganized_connected_component_segmentation.h | 2 +- .../plane_coefficient_comparator.h | 2 +- .../pcl/segmentation/sac_segmentation.h | 4 +- .../pcl/segmentation/segment_differences.h | 20 +- .../pcl/segmentation/supervoxel_clustering.h | 4 +- segmentation/src/supervoxel_clustering.cpp | 4 +- simulation/include/pcl/simulation/model.h | 2 +- .../include/pcl/simulation/range_likelihood.h | 72 +- .../include/pcl/simulation/sum_reduce.h | 2 +- simulation/src/model.cpp | 4 +- simulation/src/range_likelihood.cpp | 47 +- simulation/tools/README_about_tools | 2 +- simulation/tools/sim_test_simple.cpp | 6 +- simulation/tools/sim_viewer.cpp | 4 +- simulation/tools/simulation_io.cpp | 2 +- stereo/include/pcl/stereo/stereo_matching.h | 8 +- .../3rdparty/opennurbs/opennurbs_beam.h | 2 +- .../3rdparty/opennurbs/opennurbs_bezier.h | 2 +- .../3rdparty/opennurbs/opennurbs_polyline.h | 2 +- .../3rdparty/poisson4/bspline_data.hpp | 7 + .../pcl/surface/bilateral_upsampling.h | 2 +- surface/include/pcl/surface/concave_hull.h | 5 - surface/include/pcl/surface/gp3.h | 6 +- surface/include/pcl/surface/grid_projection.h | 2 +- .../pcl/surface/impl/bilateral_upsampling.hpp | 12 +- .../include/pcl/surface/impl/concave_hull.hpp | 14 +- .../include/pcl/surface/impl/convex_hull.hpp | 2 +- .../pcl/surface/impl/marching_cubes.hpp | 168 +- .../pcl/surface/impl/marching_cubes_hoppe.hpp | 39 +- .../pcl/surface/impl/marching_cubes_rbf.hpp | 17 +- surface/include/pcl/surface/impl/mls.hpp | 881 ++++---- surface/include/pcl/surface/marching_cubes.h | 29 +- .../pcl/surface/marching_cubes_hoppe.h | 37 +- .../include/pcl/surface/marching_cubes_rbf.h | 16 +- surface/include/pcl/surface/mls.h | 501 +++-- surface/include/pcl/surface/poisson.h | 2 +- .../3rdparty/opennurbs/opennurbs_archive.cpp | 2 +- .../3rdparty/opennurbs/opennurbs_curve.cpp | 2 +- surface/src/mls.cpp | 21 +- surface/src/on_nurbs/sequential_fitter.cpp | 4 +- test/bun0.pcd | 807 +++---- test/colored_cloud.pcd | Bin 14162 -> 32241 bytes test/common/CMakeLists.txt | 13 +- test/common/test_centroid.cpp | 74 + .../common/test_colors.cpp | 35 +- test/common/test_common.cpp | 30 +- test/common/test_eigen.cpp | 74 +- test/common/test_io.cpp | 9 +- test/common/test_transforms.cpp | 403 ++-- test/common/test_wrappers.cpp | 13 +- test/features/CMakeLists.txt | 9 +- test/features/test_cppf_estimation.cpp | 51 +- test/features/test_flare_estimation.cpp | 179 ++ test/features/test_gasd_estimation.cpp | 335 +++ test/features/test_pfh_estimation.cpp | 280 +-- test/features/test_rift_estimation.cpp | 24 +- test/features/test_shot_lrf_estimation.cpp | 2 +- test/filters/CMakeLists.txt | 3 + test/filters/test_clipper.cpp | 437 ++++ test/filters/test_filters.cpp | 430 +--- test/filters/test_sampling.cpp | 31 +- test/io/CMakeLists.txt | 5 + test/io/test_grabbers.cpp | 10 +- test/io/test_io.cpp | 396 ++-- test/io/test_octree_compression.cpp | 2 +- test/io/test_ply_io.cpp | 482 +++++ test/io/test_ply_mesh_io.cpp | 8 + test/kdtree/test_kdtree.cpp | 2 +- test/octree/CMakeLists.txt | 3 + test/octree/test_octree.cpp | 24 +- test/octree/test_octree_iterator.cpp | 1886 +++++++++++++++++ test/recognition/test_recognition_cg.cpp | 11 +- test/registration/test_registration.cpp | 9 +- test/registration/test_registration_api.cpp | 18 +- .../test_sample_consensus.cpp | 5 + .../test_sample_consensus_line_models.cpp | 15 +- test/search/test_search.cpp | 2 +- test/segmentation/test_non_linear.cpp | 2 +- test/surface/test_concave_hull.cpp | 4 - test/surface/test_gp3.cpp | 2 +- test/surface/test_marching_cubes.cpp | 24 +- test/surface/test_moving_least_squares.cpp | 6 +- test/visualization/test_visualization.cpp | 39 +- tools/CMakeLists.txt | 5 +- tools/demean_cloud.cpp | 2 +- tools/extract_feature.cpp | 5 + tools/lum.cpp | 2 +- tools/mesh2pcd.cpp | 2 +- tools/mesh_sampling.cpp | 91 +- tools/mls_smoothing.cpp | 27 +- tools/octree_viewer.cpp | 308 +-- tools/pcl_video.cpp | 4 +- tools/png2pcd.cpp | 2 +- tools/uniform_sampling.cpp | 59 +- tools/virtual_scanner.cpp | 2 +- .../impl/kld_adaptive_particle_filter_omp.hpp | 15 + .../pcl/tracking/impl/particle_filter_omp.hpp | 15 + .../pcl/tracking/impl/pyramidal_klt.hpp | 14 +- .../include/pcl/tracking/impl/tracking.hpp | 39 +- .../tracking/kld_adaptive_particle_filter.h | 4 +- .../kld_adaptive_particle_filter_omp.h | 11 +- .../pcl/tracking/particle_filter_omp.h | 15 +- tracking/include/pcl/tracking/pyramidal_klt.h | 10 +- visualization/CMakeLists.txt | 4 +- .../include/pcl/visualization/cloud_viewer.h | 3 +- .../include/pcl/visualization/common/common.h | 3 +- .../pcl/visualization/impl/pcl_visualizer.hpp | 199 +- .../pcl/visualization/interactor_style.h | 6 +- .../include/pcl/visualization/pcl_plotter.h | 2 +- .../pcl/visualization/pcl_visualizer.h | 198 +- .../point_cloud_color_handlers.h | 1 + .../point_cloud_geometry_handlers.h | 5 +- .../pcl/visualization/point_picking_event.h | 2 +- .../visualization/simple_buffer_visualizer.h | 8 +- .../vtk/vtkRenderWindowInteractorFix.mm | 2 +- .../visualization/vtk/vtkVertexBufferObject.h | 2 +- visualization/src/common/common.cpp | 19 +- visualization/src/interactor_style.cpp | 2 +- visualization/src/pcl_painter2D.cpp | 2 +- visualization/src/pcl_plotter.cpp | 2 +- visualization/src/pcl_visualizer.cpp | 860 +++++--- visualization/test/CMakeLists.txt | 12 +- visualization/test/test_shapes_multiport.cpp | 56 + visualization/test/text_simple.cpp | 24 + visualization/test/text_simple_multiport.cpp | 30 + visualization/tools/openni_image.cpp | 2 +- visualization/tools/pcd_viewer.cpp | 12 +- 694 files changed, 15803 insertions(+), 8079 deletions(-) create mode 100644 .appveyor.yml create mode 100755 .github/change_log.py create mode 100755 .github/generate_abi_reports.sh create mode 100644 .github/stale.yml delete mode 100644 common/include/pcl/ros/conversions.h create mode 100644 doc/tutorials/content/gasd_estimation.rst create mode 100644 doc/tutorials/content/images/gasd_estimation.png create mode 100644 doc/tutorials/content/images/gasd_estimation/grid.png create mode 100644 doc/tutorials/content/images/gasd_estimation/grid_top_side_bottom_view.png create mode 100644 features/include/pcl/features/flare.h create mode 100644 features/include/pcl/features/gasd.h create mode 100644 features/include/pcl/features/impl/flare.hpp create mode 100644 features/include/pcl/features/impl/gasd.hpp create mode 100644 features/src/flare.cpp rename common/include/pcl/ros/register_point_struct.h => features/src/gasd.cpp (72%) create mode 100644 io/include/pcl/io/low_level_io.h create mode 100644 pcl.png mode change 100644 => 100755 segmentation/include/pcl/segmentation/impl/segment_differences.hpp mode change 100644 => 100755 segmentation/include/pcl/segmentation/segment_differences.h rename registration/include/pcl/registration/impl/correspondence_rejection.hpp => test/common/test_colors.cpp (63%) create mode 100644 test/features/test_flare_estimation.cpp create mode 100644 test/features/test_gasd_estimation.cpp create mode 100644 test/filters/test_clipper.cpp create mode 100644 test/io/test_ply_io.cpp create mode 100644 test/octree/test_octree_iterator.cpp create mode 100644 visualization/test/test_shapes_multiport.cpp create mode 100644 visualization/test/text_simple.cpp create mode 100644 visualization/test/text_simple_multiport.cpp diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 00000000..956c8859 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,99 @@ +cache: + - C:\Tools\vcpkg\installed\ + - C:\Tools\ninja\ninja.exe + +configuration: Release + +environment: + VCPKG_DIR: C:\Tools\vcpkg + NINJA_DIR: C:\Tools\ninja + GENERATOR: Ninja + matrix: + - APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2015' + PLATFORM: x86 + VCVARSALL: '%ProgramFiles(x86)%\Microsoft Visual Studio 14.0\VC\vcvarsall.bat' + ARCHITECTURE: x86 + - APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2015' + PLATFORM: x64 + VCVARSALL: '%ProgramFiles(x86)%\Microsoft Visual Studio 14.0\VC\vcvarsall.bat' + ARCHITECTURE: x86_amd64 + #- APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2017' + # PLATFORM: x86 + # VCVARSALL: '%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat' + # ARCHITECTURE: x86 + #- APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2017' + # PLATFORM: x64 + # VCVARSALL: '%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat' + # ARCHITECTURE: x86_amd64 + +init: + - cd %VCPKG_DIR% + - git pull + - echo.set(VCPKG_BUILD_TYPE release)>> %VCPKG_DIR%\triplets\%PLATFORM%-windows.cmake + - .\bootstrap-vcpkg.bat + - vcpkg version + - cd %APPVEYOR_BUILD_FOLDER% + +install: + - vcpkg install + boost-system + boost-filesystem + boost-thread + boost-date-time + boost-iostreams + boost-chrono + boost-asio + boost-dynamic-bitset + boost-foreach + boost-graph + boost-interprocess + boost-multi-array + boost-ptr-container + boost-random + boost-signals2 + eigen3 + flann + qhull + gtest + --triplet %PLATFORM%-windows + - vcpkg list + - set PATH=%VCPKG_DIR%\installed\%PLATFORM%-windows\bin;%PATH% + - if not exist %NINJA_DIR%\ mkdir %NINJA_DIR% + - cd %NINJA_DIR% + - if not exist ninja.exe appveyor DownloadFile https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-win.zip + - if not exist ninja.exe 7z x ninja-win.zip + - set PATH=%NINJA_DIR%;%PATH% + +build: + parallel: true + +build_script: + # These tests will fails on Windows. + # Therefore, these tests are disabled until fixed. + # AppVeyor Log : https://ci.appveyor.com/project/PointCloudLibrary/pcl/build/1.0.267 + # * common_eigen + # * feature_rift_estimation + # * feature_cppf_estimation + # * feature_pfh_estimation + - call "%VCVARSALL%" %ARCHITECTURE% + - cd %APPVEYOR_BUILD_FOLDER% + - mkdir build + - cd build + - cmake -G"%GENERATOR%" + -DCMAKE_C_COMPILER=cl.exe + -DCMAKE_CXX_COMPILER=cl.exe + -DCMAKE_TOOLCHAIN_FILE=%VCPKG_DIR%\scripts\buildsystems\vcpkg.cmake + -DVCPKG_APPLOCAL_DEPS=OFF + -DPCL_BUILD_WITH_BOOST_DYNAMIC_LINKING_WIN32=ON + -DPCL_BUILD_WITH_FLANN_DYNAMIC_LINKING_WIN32=ON + -DPCL_BUILD_WITH_QHULL_DYNAMIC_LINKING_WIN32=ON + -DPCL_NO_PRECOMPILE=OFF + -DBUILD_apps=OFF + -DBUILD_examples=OFF + -DBUILD_global_tests=ON + -DBUILD_simulation=OFF + -DBUILD_tools=OFF + .. + - cmake --build . --config %CONFIGURATION% + - ctest -C %CONFIGURATION% -V + diff --git a/.github/change_log.py b/.github/change_log.py new file mode 100755 index 00000000..2305a7ce --- /dev/null +++ b/.github/change_log.py @@ -0,0 +1,448 @@ +#! /usr/bin/env python3 + +""" +Software License Agreement (BSD License) + + Point Cloud Library (PCL) - www.pointclouds.org + Copyright (c) 2018-, Open Perception, Inc. + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of the copyright holder(s) nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +""" + +import argparse +from argparse import ArgumentParser +from collections import defaultdict +import getpass +import json +import os +import pathlib +import re +import subprocess +import sys + +import requests + + +def find_pcl_folder(): + folder = os.path.dirname(os.path.abspath(__file__)) + folder = pathlib.Path(folder).parent + return str(folder) + + +def find_pr_list(start: str, end: str): + """Returns all PR ids from a certain commit range. Inspired in + http://joey.aghion.com/find-the-github-pull-request-for-a-commit/ + https://stackoverflow.com/questions/36433572/how-does-ancestry-path-work-with-git-log#36437843 + """ + + # Let git generate the proper pr history + cmd = "git log --oneline " + start + ".." + end + cmd = cmd.split() + output = subprocess.run(cmd, cwd=FOLDER, stdout=subprocess.PIPE) + pr_commits = output.stdout.split(b"\n") + + # Fetch ids for all merge requests from PRS + merge_re = re.compile("\S+ Merge pull request #(\d+) from \S+") + squash_re = re.compile("\(#(\d+)\)") + + ids = [] + for pr in pr_commits: + + pr_s = str(pr) + + # Match agains usual pattern + uid = None + match = merge_re.fullmatch(pr_s) + + # Match agains squash pattern + if not match: + match = squash_re.search(pr_s) + + # Abort + if not match: + continue + + # Extract PR uid + uid = int(match.group(1)) + ids.append(uid) + + return ids + + +def fetch_pr_info(pr_ids, auth): + + prs_url = "https://api.github.com/repos/PointCloudLibrary/pcl/pulls/" + pr_info = [] + + sys.stdout.write("Fetching PR Info: {}%".format(0)) + sys.stdout.flush() + + for i, pr_id in enumerate(pr_ids): + + # Fetch GitHub info + response = requests.get(prs_url + str(pr_id), auth=auth) + data = response.json() + + if response.status_code != 200: + print( + "\nError: Failed to fetch PR info. Server reported '" + + data["message"] + + "'", + file=sys.stderr, + ) + exit(code=1) + + d = {"id": pr_id, "title": data["title"], "labels": data["labels"]} + pr_info.append(d) + + # import pdb; pdb.set_trace() + sys.stdout.write( + "\rFetching PR Info: {:0.2f}%".format(100 * (i + 1) / len(pr_ids)) + ) + sys.stdout.flush() + + print("") + return pr_info + + +def extract_version(tag): + """Finds the corresponding version from a provided tag. + If the tag does not correspond to a suitable version tag, the original tag + is returned + """ + version_re = re.compile("pcl-\S+") + res = version_re.fullmatch(tag) + + # Not a usual version tag + if not res: + return tag + + return tag[4:] + + +def generate_text_content(tag, pr_info): + + module_order = ( + None, + "cmake", + "2d", + "common", + "cuda", + "features", + "filters", + "geometry", + "gpu", + "io", + "kdtree", + "keypoints", + "ml", + "octree", + "outofcore", + "people", + "recognition", + "registration", + "sample_consensus", + "search", + "segmentation", + "simulation", + "stereo", + "surface", + "apps", + "docs", + "tutorials", + "examples", + "tests", + "tools", + "ci", + ) + + module_titles = { + None: "Uncategorized", + "2d": "libpcl_2d", + "apps": "PCL Apps", + "cmake": "CMake", + "ci": "CI", + "common": "libpcl_common", + "cuda": "libpcl_cuda", + "docs": "PCL Docs", + "examples": "PCL Examples", + "features": "libpcl_features", + "filters": "libpcl_filters", + "gpu": "libpcl_gpu", + "io": "libpcl_io", + "kdtree": "libpcl_kdtree", + "keypoints": "libpcl_keypoints", + "ml": "libpcl_ml", + "octree": "libpcl_octree", + "outofcore": "libpcl_outofcore", + "people": "libpcl_people", + "recognition": "libpcl_recognition", + "registration": "libpcl_registration", + "sample_consensus": "libpcl_sample_consensus", + "search": "libpcl_search", + "segmentation": "libpcl_segmentation", + "simulation": "libpcl_simulation", + "stereo": "libpcl_stereo", + "surface": "libpcl_surface", + "tests": "PCL Tests", + "tools": "PCL Tools", + "tutorials": "PCL Tutorials", + "visualization": "libpcl_visualization", + } + + changes_order = ("new-feature", "deprecation", "removal", "behavior", "api", "abi") + + changes_titles = { + "new-feature": "New Features", + "deprecation": "Deprecated", + "removal": "Removed", + "behavior": "Behavioral changes", + "api": "API changes", + "abi": "ABI changes", + } + + changes_description = { + "new-feature": "Newly added functionalities.", + "deprecation": "Deprecated code scheduled to be removed after two minor releases.", + "removal": "Removal of deprecated code.", + "behavior": "Changes in the expected default behavior.", + "api": "Changes to the API which didn't went through the proper deprecation and removal cycle.", + "abi": "Changes that cause ABI incompatibility but are still API compatible.", + } + + changes_labels = { + "breaks API": "api", + "breaks ABI": "abi", + "behavior": "behavior", + "deprecation": "deprecation", + "removal": "removal", + } + + # change_log content + clog = [] + + # Infer version from tag + version = extract_version(tag) + + # Find the commit date for writting the Title + cmd = ("git log -1 --format=%ai " + tag).split() + output = subprocess.run(cmd, cwd=FOLDER, stdout=subprocess.PIPE) + date = output.stdout.split()[0].decode() + tokens = date.split("-") + clog += [ + "## *= " + + version + + " (" + + tokens[2] + + "." + + tokens[1] + + "." + + tokens[0] + + ") =*" + ] + + # Map each PR into the approriate module and changes + modules = defaultdict(list) + changes = defaultdict(list) + module_re = re.compile("module: \S+") + changes_re = re.compile("changes: ") + feature_re = re.compile("new feature") + + for pr in pr_info: + + pr["modules"] = [] + pr["changes"] = [] + + for label in pr["labels"]: + if module_re.fullmatch(label["name"]): + module = label["name"][8:] + pr["modules"].append(module) + modules[module].append(pr) + + elif changes_re.match(label["name"]): + key = changes_labels[label["name"][9:]] + pr["changes"].append(key) + changes[key].append(pr) + + elif feature_re.fullmatch(label["name"]): + pr["changes"].append("new-feature") + changes["new-feature"].append(pr) + + # No labels defaults to section None + if not pr["modules"]: + modules[None].append(pr) + continue + + # Generate Changes Summary + for key in changes_order: + + # Skip empty sections + if not changes[key]: + continue + + clog += ["\n### `" + changes_titles[key] + ":`\n"] + + clog += ["*" + changes_description[key] + "*\n"] + + for pr in changes[key]: + prefix = "".join(["[" + k + "]" for k in pr["modules"]]) + if prefix: + prefix = "**" + prefix + "** " + clog += [ + "* " + + prefix + + pr["title"] + + " [[#" + + str(pr["id"]) + + "]]" + + "(https://github.com/PointCloudLibrary/pcl/pull/" + + str(pr["id"]) + + ")" + ] + + # Traverse Modules and generate each section's content + clog += ["\n### `Modules:`"] + for key in module_order: + + # Skip empty sections + if not modules[key]: + continue + + # if key: + clog += ["\n#### `" + module_titles[key] + ":`\n"] + + for pr in modules[key]: + prefix = "".join(["[" + k + "]" for k in pr["changes"]]) + if prefix: + prefix = "**" + prefix + "** " + clog += [ + "* " + + prefix + + pr["title"] + + " [[#" + + str(pr["id"]) + + "]]" + + "(https://github.com/PointCloudLibrary/pcl/pull/" + + str(pr["id"]) + + ")" + ] + + return clog + + +def parse_arguments(): + + parser = ArgumentParser( + description="Generate a change log between two " + "revisions.\n\nCheck https://github.com/PointCloudLibrary/pcl/wiki/Preparing-Releases#creating-the-change-log " + "for some additional examples on how to use the tool." + ) + parser.add_argument( + "start", + help="The start (exclusive) " "revision/commit/tag to generate the log.", + ) + parser.add_argument( + "end", + nargs="?", + default="HEAD", + help="The end " + "(inclusive) revision/commit/tag to generate the log. " + "(Defaults to HEAD)", + ) + parser.add_argument( + "--username", + help="GitHub Account user name. If " + "specified it will perform requests with the provided credentials. " + "This is often required in order to overcome GitHub API's request " + "limits.", + ) + meg = parser.add_mutually_exclusive_group() + meg.add_argument( + "--cache", + nargs="?", + const="pr_info.json", + metavar="FILE", + help="Caches the PR info extracted from GitHub into a JSON file. " + "(Defaults to 'pr_info.json')", + ) + meg.add_argument( + "--from-cache", + nargs="?", + const="pr_info.json", + metavar="FILE", + help="Uses a previously generated PR info JSON cache " + "file to generate the change log. (Defaults to 'pr_info.json')", + ) + + # Parse arguments + args = parser.parse_args() + args.auth = None + + if args.username: + password = getpass.getpass(prompt="Password for " + args.username + ": ") + args.auth = (args.username, password) + + return args + + +## +## 'main' +## + +FOLDER = find_pcl_folder() + +# Parse arguments +args = parse_arguments() + +pr_info = None +if not args.from_cache: + + # Find all PRs since tag + prs = find_pr_list(start=args.start, end=args.end) + + # Generate pr objects with title, labels from ids + pr_info = fetch_pr_info(prs, auth=args.auth) + if args.cache: + with open(args.cache, "w") as fp: + d = {"start": args.start, "end": args.end, "pr_info": pr_info} + fp.write(json.dumps(d)) +else: + # Load previously cached info + with open(args.from_cache) as fp: + d = json.loads(fp.read()) + pr_info = d["pr_info"] + args.start = d["start"] + args.end = d["start"] + + +# Generate text content based on changes +clog = generate_text_content(tag=args.end, pr_info=pr_info) +print("\n".join(clog)) diff --git a/.github/generate_abi_reports.sh b/.github/generate_abi_reports.sh new file mode 100755 index 00000000..20cd2c0f --- /dev/null +++ b/.github/generate_abi_reports.sh @@ -0,0 +1,144 @@ +#! /bin/bash + +if ! hash cmake 2>/dev/null \ + || ! hash abi-dumper 2>/dev/null \ + || ! hash abi-compliance-checker 2>/dev/null; then + echo "This script requires cmake, abi-dumper and abi-compliance-checker" + echo "On Ubuntu: apt-get install cmake abi-dumper abi-compliance-checker" + exit 1 +fi + +usage () +{ + echo "Usage: $0 [-o ] [-j ] " 1>&2; + exit 1; +} + +# Set defaults +ROOT_FOLDER="$(pwd)/abi-reports" +N_WORKERS=4 + +# Parse arguments +while getopts ":o:j:" o; do + case "${o}" in + o) + ROOT_FOLDER=${OPTARG} + ;; + j) + N_WORKERS=${OPTARG} + ;; + *) + usage + ;; + esac +done + +# Shift positional arguments +shift $((OPTIND-1)) + +# Check if positional arguments were parsed +OLD_VERSION=$1 +NEW_VERSION=$2 + +if [ -z "${OLD_VERSION}" ] || [ -z "${NEW_VERSION}" ]; then + usage + exit 1 +fi + +# create the top folders +mkdir -p "$ROOT_FOLDER" +cd "$ROOT_FOLDER" +PCL_FOLDER="$ROOT_FOLDER/pcl" + +if [ ! -d "$PCL_FOLDER" ]; then + # Clone if needed + git clone https://github.com/PointCloudLibrary/pcl.git +fi + +function build_and_dump +{ + # Store current folder + local PWD=$(pwd) + + # If it is a version prepend pcl- + local TAG=$1 + if [ $(echo "$1" | grep "^[0-9]\+\.[0-9]\+\.[0-9]\+$") ]; then + TAG="pcl-$TAG" + fi + + # Checkout correct version + cd "$PCL_FOLDER" + git checkout $TAG + + # Escape version + local VERSION_ESCAPED=$(echo "$1" | sed -e 's/\./_/g') + + # Create the install folders + local INSTALL_FOLDER="$ROOT_FOLDER/install/$VERSION_ESCAPED" + mkdir -p "$INSTALL_FOLDER" + + # create the build folders + local BUILD_FOLDER="$ROOT_FOLDER/build/$VERSION_ESCAPED" + mkdir -p "$BUILD_FOLDER" + cd "$BUILD_FOLDER" + + # Build + cmake -DCMAKE_BUILD_TYPE="Debug" \ + -DCMAKE_CXX_FLAGS="-Og" \ + -DCMAKE_C_FLAGS="-Og" \ + -DCMAKE_INSTALL_PREFIX="$INSTALL_FOLDER" \ + -DPCL_ONLY_CORE_POINT_TYPES=ON \ + -DBUILD_tools=OFF \ + -DBUILD_global_tests=OFF \ + "$PCL_FOLDER" + # -DBUILD_2d=OFF \ + # -DBUILD_features=OFF \ + # -DBUILD_filters=OFF \ + # -DBUILD_geometry=OFF \ + # -DBUILD_io=OFF \ + # -DBUILD_kdtree=OFF \ + # -DBUILD_keypoints=OFF \ + # -DBUILD_ml=OFF \ + # -DBUILD_octree=OFF \ + # -DBUILD_outofcore=OFF \ + # -DBUILD_people=OFF \ + # -DBUILD_recognition=OFF \ + # -DBUILD_registration=OFF \ + # -DBUILD_sample_consensus=OFF \ + # -DBUILD_search=OFF \ + # -DBUILD_segmentation=OFF \ + # -DBUILD_stereo=OFF \ + # -DBUILD_surface=OFF \ + # -DBUILD_tracking=OFF \ + # -DBUILD_visualization=OFF \ + make -j${N_WORKERS} install + + # create ABI dump folder + local ABI_DUMP_FOLDER="$ROOT_FOLDER/abi_dump/$VERSION_ESCAPED" + mkdir -p "$ABI_DUMP_FOLDER" + + # Sweep through available modules + cd "$INSTALL_FOLDER/lib" + ls *.so | sed -e "s/^libpcl_//" -e "s/.so//" \ + | awk '{print "libpcl_"$1".so -o '"$ABI_DUMP_FOLDER"'/"$1".dump -lver '"$1"'"}' \ + | xargs -n5 -P${N_WORKERS} abi-dumper + + # Restore folder + cd $PWD +} + +build_and_dump "$OLD_VERSION" +build_and_dump "$NEW_VERSION" + + +# Move to root folder and generate reports +OLD_VERSION_ESCAPED=$(echo "$OLD_VERSION" | sed -e 's/\./_/g') +NEW_VERSION_ESCAPED=$(echo "$NEW_VERSION" | sed -e 's/\./_/g') +OLD_INSTALL_FOLDER="$ROOT_FOLDER/install/$OLD_VERSION_ESCAPED" +OLD_ABI_DUMP_FOLDER="$ROOT_FOLDER/abi_dump/$OLD_VERSION_ESCAPED" +NEW_ABI_DUMP_FOLDER="$ROOT_FOLDER/abi_dump/$NEW_VERSION_ESCAPED" + +cd "$ROOT_FOLDER" +find $OLD_INSTALL_FOLDER/lib -name '*.so' -printf '%P\n' | sed -e "s/^libpcl_//" -e "s/.so//" \ + | awk '{print "-lib "$1" -old '"$OLD_ABI_DUMP_FOLDER"'/"$1".dump -new '"$NEW_ABI_DUMP_FOLDER"'/"$1".dump"}' \ + | xargs -n6 -P${N_WORKERS} abi-compliance-checker diff --git a/.github/issue_template.md b/.github/issue_template.md index c3ea4d67..9157557d 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -1,4 +1,4 @@ -:warning: This is a issue tracker, please use our mailing list for questions: www.pcl-users.org. :warning: + @@ -8,6 +8,10 @@ * Compiler: * PCL Version: +## Context + + + ## Expected Behavior @@ -16,15 +20,10 @@ -## Possible Solution - - - ## Code to Reproduce -## Context - - - +## Possible Solution + + diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 00000000..595c3ad5 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,28 @@ +# Configuration for probot-stale - https://github.com/probot/stale + +# Number of days of inactivity before an Issue or Pull Request becomes stale +daysUntilStale: 60 +# Number of days of inactivity before a stale Issue or Pull Request is closed +# False means never close +daysUntilClose: false +# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable +exemptLabels: + - "status: needs review" + - "status: needs testing" + - "status: needs decision" + - "status: ready to merge" +# Label to use when marking as stale +staleLabel: "status: stale" +# Comment to post when marking as stale. Set to `false` to disable +markComment: | + This pull request has been automatically marked as stale because it hasn't had + any activity in the past 60 days. Commenting or adding a new commit to the + pull request will revert this. + + Come back whenever you have time. We look forward to your contribution. +# Comment to post when removing the stale label. Set to `false` to disable +unmarkComment: false +# Comment to post when closing a stale Issue or Pull Request. Set to `false` to disable +closeComment: false +# Limit to only `issues` or `pulls` +only: pulls diff --git a/.travis.sh b/.travis.sh index 226e4629..02959aa9 100755 --- a/.travis.sh +++ b/.travis.sh @@ -27,305 +27,54 @@ function before_install () fi } -function build () -{ - case $CC in - clang ) build_lib;; - gcc ) build_lib_core;; - esac -} - -function build_lib () +function build_all () { # A complete build # Configure mkdir $BUILD_DIR && cd $BUILD_DIR cmake -DCMAKE_C_FLAGS="$CMAKE_C_FLAGS" -DCMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS" \ -DPCL_ONLY_CORE_POINT_TYPES=ON \ - -DPCL_QT_VERSION=4 \ - -DBUILD_simulation=ON \ - -DBUILD_global_tests=OFF \ - -DBUILD_examples=OFF \ - -DBUILD_tools=OFF \ - -DBUILD_apps=OFF \ - -DBUILD_apps_3d_rec_framework=OFF \ - -DBUILD_apps_cloud_composer=OFF \ - -DBUILD_apps_in_hand_scanner=OFF \ - -DBUILD_apps_modeler=OFF \ - -DBUILD_apps_optronic_viewer=OFF \ - -DBUILD_apps_point_cloud_editor=OFF \ - $PCL_DIR - # Build - make -j2 -} - -function build_examples () -{ - # A complete build - # Configure - mkdir $BUILD_DIR && cd $BUILD_DIR - cmake -DCMAKE_C_FLAGS="$CMAKE_C_FLAGS" -DCMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS" \ - -DPCL_ONLY_CORE_POINT_TYPES=ON \ - -DPCL_QT_VERSION=4 \ + -DPCL_QT_VERSION=5 \ -DBUILD_simulation=ON \ -DBUILD_global_tests=OFF \ -DBUILD_examples=ON \ - -DBUILD_tools=OFF \ - -DBUILD_apps=OFF \ - $PCL_DIR - # Build - make -j2 -} - -function build_tools () -{ - # A complete build - # Configure - mkdir $BUILD_DIR && cd $BUILD_DIR - cmake -DCMAKE_C_FLAGS="$CMAKE_C_FLAGS" -DCMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS" \ - -DPCL_ONLY_CORE_POINT_TYPES=ON \ - -DPCL_QT_VERSION=4 \ - -DBUILD_simulation=ON \ - -DBUILD_global_tests=OFF \ - -DBUILD_examples=OFF \ -DBUILD_tools=ON \ - -DBUILD_apps=OFF \ - $PCL_DIR - # Build - make -j2 -} - -function build_apps () -{ - # A complete build - # Configure - mkdir $BUILD_DIR && cd $BUILD_DIR - cmake -DCMAKE_C_FLAGS="$CMAKE_C_FLAGS" -DCMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS" \ - -DPCL_ONLY_CORE_POINT_TYPES=ON \ - -DPCL_QT_VERSION=4 \ - -DBUILD_simulation=OFF \ - -DBUILD_outofcore=OFF \ - -DBUILD_people=OFF \ - -DBUILD_global_tests=OFF \ - -DBUILD_examples=OFF \ - -DBUILD_tools=OFF \ -DBUILD_apps=ON \ -DBUILD_apps_3d_rec_framework=ON \ -DBUILD_apps_cloud_composer=ON \ -DBUILD_apps_in_hand_scanner=ON \ -DBUILD_apps_modeler=ON \ - -DBUILD_apps_optronic_viewer=OFF \ -DBUILD_apps_point_cloud_editor=ON \ $PCL_DIR # Build make -j2 } -function build_lib_core () -{ - # A reduced build, only pcl_common - # Configure - mkdir $BUILD_DIR && cd $BUILD_DIR - cmake -DCMAKE_C_FLAGS="$CMAKE_C_FLAGS" -DCMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS" \ - -DPCL_ONLY_CORE_POINT_TYPES=ON \ - -DBUILD_2d=OFF \ - -DBUILD_features=OFF \ - -DBUILD_filters=OFF \ - -DBUILD_geometry=OFF \ - -DBUILD_global_tests=OFF \ - -DBUILD_io=OFF \ - -DBUILD_kdtree=OFF \ - -DBUILD_keypoints=OFF \ - -DBUILD_ml=OFF \ - -DBUILD_octree=OFF \ - -DBUILD_outofcore=OFF \ - -DBUILD_people=OFF \ - -DBUILD_recognition=OFF \ - -DBUILD_registration=OFF \ - -DBUILD_sample_consensus=OFF \ - -DBUILD_search=OFF \ - -DBUILD_segmentation=OFF \ - -DBUILD_stereo=OFF \ - -DBUILD_surface=OFF \ - -DBUILD_tools=OFF \ - -DBUILD_tracking=OFF \ - -DBUILD_visualization=OFF \ - $PCL_DIR - # Build - make -j2 -} - -function test_core () +function test_all () { + # A complete build # Configure mkdir $BUILD_DIR && cd $BUILD_DIR cmake -DCMAKE_C_FLAGS="$CMAKE_C_FLAGS" -DCMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS" \ -DPCL_ONLY_CORE_POINT_TYPES=ON \ - -DPCL_NO_PRECOMPILE=ON \ - -DBUILD_tools=OFF \ - -DBUILD_examples=OFF \ - -DBUILD_apps=OFF \ - -DBUILD_2d=ON \ - -DBUILD_features=ON \ - -DBUILD_filters=ON \ - -DBUILD_geometry=ON \ - -DBUILD_io=ON \ - -DBUILD_kdtree=ON \ - -DBUILD_keypoints=ON \ - -DBUILD_ml=OFF \ - -DBUILD_octree=ON \ - -DBUILD_outofcore=OFF \ - -DBUILD_people=OFF \ - -DBUILD_recognition=OFF \ - -DBUILD_registration=OFF \ - -DBUILD_sample_consensus=ON \ - -DBUILD_search=ON \ - -DBUILD_segmentation=OFF \ - -DBUILD_simulation=OFF \ - -DBUILD_stereo=OFF \ - -DBUILD_surface=OFF \ - -DBUILD_tracking=OFF \ - -DBUILD_visualization=OFF \ + -DPCL_QT_VERSION=4 \ + -DBUILD_simulation=ON \ -DBUILD_global_tests=ON \ - -DBUILD_tests_2d=ON \ - -DBUILD_tests_common=ON \ - -DBUILD_tests_features=ON \ - -DBUILD_tests_filters=OFF \ - -DBUILD_tests_geometry=ON \ - -DBUILD_tests_io=OFF \ - -DBUILD_tests_kdtree=ON \ - -DBUILD_tests_keypoints=ON \ - -DBUILD_tests_octree=ON \ - -DBUILD_tests_outofcore=OFF \ - -DBUILD_tests_people=OFF \ - -DBUILD_tests_recognition=OFF \ - -DBUILD_tests_registration=OFF \ - -DBUILD_tests_sample_consensus=ON \ - -DBUILD_tests_search=ON \ - -DBUILD_tests_segmentation=OFF \ - -DBUILD_tests_surface=OFF \ - -DBUILD_tests_visualization=OFF \ - $PCL_DIR - # Build and run tests - make -j2 tests -} - -function test_ext_1 () -{ - # Configure - mkdir $BUILD_DIR && cd $BUILD_DIR - cmake -DCMAKE_C_FLAGS="$CMAKE_C_FLAGS" -DCMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS" \ - -DPCL_ONLY_CORE_POINT_TYPES=ON \ - -DPCL_NO_PRECOMPILE=ON \ - -DBUILD_tools=OFF \ -DBUILD_examples=OFF \ - -DBUILD_apps=OFF \ - -DBUILD_2d=ON \ - -DBUILD_features=ON \ - -DBUILD_filters=ON \ - -DBUILD_geometry=ON \ - -DBUILD_io=ON \ - -DBUILD_kdtree=ON \ - -DBUILD_keypoints=OFF \ - -DBUILD_ml=OFF \ - -DBUILD_octree=ON \ - -DBUILD_outofcore=ON \ - -DBUILD_people=OFF \ - -DBUILD_recognition=OFF \ - -DBUILD_registration=ON \ - -DBUILD_sample_consensus=ON \ - -DBUILD_search=ON \ - -DBUILD_segmentation=OFF \ - -DBUILD_simulation=OFF \ - -DBUILD_stereo=OFF \ - -DBUILD_surface=ON \ - -DBUILD_tracking=OFF \ - -DBUILD_visualization=ON \ - -DBUILD_global_tests=ON \ - -DBUILD_tests_2d=OFF \ - -DBUILD_tests_common=OFF \ - -DBUILD_tests_features=OFF \ - -DBUILD_tests_filters=OFF \ - -DBUILD_tests_geometry=OFF \ - -DBUILD_tests_io=ON \ - -DBUILD_tests_kdtree=OFF \ - -DBUILD_tests_keypoints=OFF \ - -DBUILD_tests_octree=OFF \ - -DBUILD_tests_outofcore=ON \ - -DBUILD_tests_people=OFF \ - -DBUILD_tests_recognition=OFF \ - -DBUILD_tests_registration=ON \ - -DBUILD_tests_sample_consensus=OFF \ - -DBUILD_tests_search=OFF \ - -DBUILD_tests_segmentation=OFF \ - -DBUILD_tests_surface=ON \ - -DBUILD_tests_visualization=ON \ - $PCL_DIR - # Build and run tests - make -j2 tests -} - -function test_ext_2 () -{ - # Configure - mkdir $BUILD_DIR && cd $BUILD_DIR - cmake -DCMAKE_C_FLAGS="$CMAKE_C_FLAGS" -DCMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS" \ - -DPCL_ONLY_CORE_POINT_TYPES=ON \ - -DPCL_NO_PRECOMPILE=ON \ -DBUILD_tools=OFF \ - -DBUILD_examples=OFF \ -DBUILD_apps=OFF \ - -DBUILD_2d=ON \ - -DBUILD_features=ON \ - -DBUILD_filters=ON \ - -DBUILD_geometry=ON \ - -DBUILD_io=ON \ - -DBUILD_kdtree=ON \ - -DBUILD_keypoints=OFF \ - -DBUILD_ml=ON \ - -DBUILD_octree=ON \ - -DBUILD_outofcore=OFF \ - -DBUILD_people=ON \ - -DBUILD_recognition=ON \ - -DBUILD_registration=ON \ - -DBUILD_sample_consensus=ON \ - -DBUILD_search=ON \ - -DBUILD_segmentation=ON \ - -DBUILD_simulation=OFF \ - -DBUILD_stereo=OFF \ - -DBUILD_surface=OFF \ - -DBUILD_tracking=OFF \ - -DBUILD_visualization=ON \ - -DBUILD_global_tests=ON \ - -DBUILD_tests_2d=OFF \ - -DBUILD_tests_common=OFF \ - -DBUILD_tests_features=OFF \ - -DBUILD_tests_filters=ON \ - -DBUILD_tests_geometry=OFF \ - -DBUILD_tests_io=OFF \ - -DBUILD_tests_kdtree=OFF \ - -DBUILD_tests_keypoints=OFF \ - -DBUILD_tests_octree=OFF \ - -DBUILD_tests_outofcore=OFF \ - -DBUILD_tests_people=ON \ - -DBUILD_tests_recognition=ON \ - -DBUILD_tests_registration=OFF \ - -DBUILD_tests_sample_consensus=OFF \ - -DBUILD_tests_search=OFF \ - -DBUILD_tests_segmentation=ON \ - -DBUILD_tests_surface=OFF \ - -DBUILD_tests_visualization=OFF \ $PCL_DIR - # Build and run tests + # Build make -j2 tests } + function doc () { - # Do not generate documentation for pull requests - if [[ $TRAVIS_PULL_REQUEST != 'false' ]]; then exit; fi # Install sphinx - pip install --user sphinx pyparsing==2.1.9 sphinxcontrib-doxylink + pip3 install --user setuptools + pip3 install --user Jinja2==2.8.1 sphinx sphinxcontrib-doxylink + # Configure mkdir $BUILD_DIR && cd $BUILD_DIR cmake -DDOXYGEN_USE_SHORT_NAMES=OFF \ @@ -337,6 +86,19 @@ function doc () git config --global user.email "documentation@pointclouds.org" git config --global user.name "PointCloudLibrary (via TravisCI)" + cd $DOC_DIR + git clone https://github.com/PointCloudLibrary/documentation.git . + + # Generate documentation and tutorials + cd $BUILD_DIR + make doc tutorials advanced + + # Do not push documentation in pull requests + if [[ $TRAVIS_EVENT_TYPE == 'pull_request' ]] ; then exit; fi + + # update the remote url to git-ssh protocol for commit + git remote set-url origin git@github.com:PointCloudLibrary/documentation.git + if [ -z "$id_rsa_{1..23}" ]; then echo 'No $id_rsa_{1..23} found !' ; exit 1; fi echo -n $id_rsa_{1..23} >> ~/.ssh/travis_rsa_64 @@ -346,13 +108,6 @@ function doc () echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config - cd $DOC_DIR - git clone git@github.com:PointCloudLibrary/documentation.git . - - # Generate documentation and tutorials - cd $BUILD_DIR - make doc tutorials advanced - # Upload to GitHub if generation succeeded if [[ $? == 0 ]]; then # Copy generated tutorials to the doc directory @@ -370,12 +125,8 @@ function doc () case $1 in before-install ) before_install;; - build ) build;; - build-examples ) build_examples;; - build-tools ) build_tools;; - build-apps ) build_apps;; - test-core ) test_core;; - test-ext-1 ) test_ext_1;; - test-ext-2 ) test_ext_2;; + build ) build_all;; + test ) test_all;; doc ) doc;; esac + diff --git a/.travis.yml b/.travis.yml index 329b46e9..69201492 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,32 +1,34 @@ sudo: required +dist: xenial language: cpp cache: ccache: true +git: + depth: 1 addons: apt: - sources: - - kalakris-cmake - - boost-latest - - kubuntu-backports - - sourceline: 'ppa:kedazo/doxygen-updates-precise' - - sourceline: 'ppa:v-launchpad-jochen-sprickerhof-de/pcl' packages: - cmake - - libboost1.55-all-dev + - libboost-filesystem-dev + - libboost-iostreams-dev + - libboost-thread-dev + - libboost-chrono-dev - libeigen3-dev - libgtest-dev - doxygen-latex - dvipng - libusb-1.0-0-dev - libqhull-dev - - libvtk5-dev + - libvtk6-dev + - libvtk6-qt-dev - libflann-dev - doxygen - - libqt4-dev - - libqt4-opengl-dev - - libvtk5-qt4-dev + - qtbase5-dev + - libqt5opengl5-dev - libglew-dev - libopenni-dev + - python3-pip + - libproj-dev #missing dependency from vtk? before_install: - bash .travis.sh before-install @@ -56,34 +58,21 @@ env: - secure: LNsNoBvqY/jYDoBjWCE5cM+f1H8xOwSBc/tbWZo6E/jPRjUOLzXSicMMUMrlVto+bFzSUT8OVajV3XmoRx+Qntzv6bDSAGjdycvHd2YZQPn8BYrsFtR4So7SsJkF9FlxzbiOXaiSRpwGn7TP/DO7Neubrr4IS2ef4nWowGrnCE8= - secure: PZivWbaCWFA2BFFY8n3UMxdEWjz7rBh568u9LF5LH3HgWADnfiwWzNriACqX9fhe7tSmDru5Bk978s+xPPAY9v24cfiDEX5a5MQ/XVr2rP48n3vlUDWERDhIodJ73F9F9GGZXToGdNz0MBUAHgiv7Lb0GYUfmOYzUJjWghngLBw= + jobs: include: - - stage: Core Build - env: TASK="build" + - name: "Documentation" compiler: gcc - script: bash .travis.sh $TASK - - env: TASK="build" - compiler: clang - script: bash .travis.sh $TASK - - stage: Extended Build and Tests - compiler: clang - env: TASK="build-examples" - script: bash .travis.sh $TASK - - compiler: clang - env: TASK="build-tools" - script: bash .travis.sh $TASK - # - compiler: clang - # env: TASK="build-apps" - # script: bash .travis.sh $TASK - - compiler: gcc env: TASK="doc" script: bash .travis.sh $TASK - - compiler: gcc - env: TASK="test-core" - script: bash .travis.sh $TASK - - compiler: gcc - env: TASK="test-ext-1" + - name: "Library, Examples, Tools, Apps" + compiler: clang + env: TASK="build" script: bash .travis.sh $TASK - - compiler: gcc - env: TASK="test-ext-2" + - name: "Unit Tests" + compiler: gcc + env: TASK="test" script: bash .travis.sh $TASK + +notifications: + email: false diff --git a/2d/CMakeLists.txt b/2d/CMakeLists.txt index afaa9984..5affe826 100644 --- a/2d/CMakeLists.txt +++ b/2d/CMakeLists.txt @@ -1,6 +1,6 @@ set(SUBSYS_NAME 2d) set(SUBSYS_DESC "Point cloud 2d") -set(SUBSYS_DEPS common io filters) +set(SUBSYS_DEPS common filters) set(build TRUE) PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON) @@ -35,6 +35,9 @@ if(build) include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include" ${VTK_INCLUDE_DIRECTORIES}) + set(LIB_NAME "pcl_${SUBSYS_NAME}") + PCL_MAKE_PKGCONFIG_HEADER_ONLY("${LIB_NAME}" "${SUBSYS_NAME}" "${SUBSYS_DESC}" "${SUBSYS_DEPS}" "" "" "" "") + #Install include files PCL_ADD_INCLUDES("${SUBSYS_NAME}" "${SUBSYS_NAME}" ${incs}) PCL_ADD_INCLUDES("${SUBSYS_NAME}" "${SUBSYS_NAME}/impl" ${impl_incs}) diff --git a/2d/include/pcl/2d/edge.h b/2d/include/pcl/2d/edge.h index 78b1af36..a0e059ed 100644 --- a/2d/include/pcl/2d/edge.h +++ b/2d/include/pcl/2d/edge.h @@ -76,7 +76,7 @@ namespace pcl /** \brief This function suppresses the edges which don't form a local maximum * in the edge direction. * \param[in] edges point cloud containing all the edges - * \param[out] maxima point cloud containing the non-max supressed edges + * \param[out] maxima point cloud containing the non-max suppressed edges * \param[in] tLow */ void @@ -298,6 +298,8 @@ namespace pcl { input_ = input; } + + EIGEN_MAKE_ALIGNED_OPERATOR_NEW }; } #include diff --git a/2d/include/pcl/2d/impl/edge.hpp b/2d/include/pcl/2d/impl/edge.hpp index bef23e63..24756609 100644 --- a/2d/include/pcl/2d/impl/edge.hpp +++ b/2d/include/pcl/2d/impl/edge.hpp @@ -352,7 +352,7 @@ pcl::Edge::detectEdgeCanny (pcl::PointCloud &out convolution_.filter (*smoothed_cloud); //PCL_ERROR ("Gaussian blur: %g\n", tt.toc ()); tt.tic (); - // Edge detection usign Sobel + // Edge detection using Sobel pcl::PointCloud::Ptr edges (new pcl::PointCloud); setInputCloud (smoothed_cloud); detectEdgeSobel (*edges); @@ -431,7 +431,7 @@ pcl::Edge::canny ( convolution_.filter (smoothed_cloud_y); - // Edge detection usign Sobel + // Edge detection using Sobel pcl::PointCloud::Ptr edges (new pcl::PointCloud); sobelMagnitudeDirection (smoothed_cloud_x, smoothed_cloud_y, *edges.get ()); diff --git a/2d/include/pcl/2d/impl/morphology.hpp b/2d/include/pcl/2d/impl/morphology.hpp index 6528eed9..39a548bc 100644 --- a/2d/include/pcl/2d/impl/morphology.hpp +++ b/2d/include/pcl/2d/impl/morphology.hpp @@ -77,7 +77,7 @@ pcl::Morphology::erosionBinary (pcl::PointCloud &output) { continue; } - // If one of the elements of the kernel and image dont match, + // If one of the elements of the kernel and image don't match, // the output image is 0. So, move to the next point. if ((*input_)(j + l - kernel_width / 2, i + k - kernel_height / 2).intensity != 1) { @@ -192,7 +192,7 @@ pcl::Morphology::erosionGray (pcl::PointCloud &output) { continue; } - // If one of the elements of the kernel and image dont match, + // If one of the elements of the kernel and image don't match, // the output image is 0. So, move to the next point. if ((*input_)(j + l - kernel_width / 2, i + k - kernel_height / 2).intensity < min || min == -1) { @@ -236,7 +236,7 @@ pcl::Morphology::dilationGray (pcl::PointCloud &output) { continue; } - // If one of the elements of the kernel and image dont match, + // If one of the elements of the kernel and image don't match, // the output image is 0. So, move to the next point. if ((*input_)(j + l - kernel_width / 2, i + k - kernel_height / 2).intensity > max || max == -1) { @@ -277,7 +277,7 @@ pcl::Morphology::subtractionBinary ( const pcl::PointCloud &input1, const pcl::PointCloud &input2) { - const int height = (input1.height < input2.hieght) ? input1.height : input2.height; + const int height = (input1.height < input2.height) ? input1.height : input2.height; const int width = (input1.width < input2.width) ? input1.width : input2.width; output.width = width; output.height = height; @@ -299,7 +299,7 @@ pcl::Morphology::unionBinary ( const pcl::PointCloud &input1, const pcl::PointCloud &input2) { - const int height = (input1.height < input2.hieght) ? input1.height : input2.height; + const int height = (input1.height < input2.height) ? input1.height : input2.height; const int width = (input1.width < input2.width) ? input1.width : input2.width; output.width = width; output.height = height; diff --git a/2d/src/examples.cpp b/2d/src/examples.cpp index 951cfcba..c90f685c 100644 --- a/2d/src/examples.cpp +++ b/2d/src/examples.cpp @@ -38,10 +38,10 @@ #include -#include -#include -#include -#include +#include +#include +#include +#include #include using namespace pcl; diff --git a/CHANGES.md b/CHANGES.md index 44b1b121..f1164447 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,478 @@ # ChangeList +## *= 1.9.0 (06.11.2018) =* + +### `New Features:` + +*Newly added functionalities.* + +* **[common][visualization]** Add Viridis color LUT [[#2420]](https://github.com/PointCloudLibrary/pcl/pull/2420) +* **[octree]** Implementation of the iterator 'OctreeLeafNodeBreadthIterator'. [[#2204]](https://github.com/PointCloudLibrary/pcl/pull/2204) +* **[octree]** Implementation of the iterator 'OctreeFixedDepthIterator'. [[#1983]](https://github.com/PointCloudLibrary/pcl/pull/1983) +* **[ci]** Enable Global Tests on Windows CI [[#2137]](https://github.com/PointCloudLibrary/pcl/pull/2137) +* **[features]** Add GASD global point cloud descriptor [[#1652]](https://github.com/PointCloudLibrary/pcl/pull/1652) +* **[visualization]** Add overload to `PCLVisualizer::addText3D()` that allows specifying text orientation [[#2038]](https://github.com/PointCloudLibrary/pcl/pull/2038) +* **[features]** FLARELocalReferenceFrameEstimation class added [[#1571]](https://github.com/PointCloudLibrary/pcl/pull/1571) +* **[surface][tools]** Add new mls projection method. Deprecated `MovingLeastSquares::setPolynomialFit()`. [[#1960]](https://github.com/PointCloudLibrary/pcl/pull/1960) + +### `Deprecated:` + +*Deprecated code scheduled to be removed after two minor releases.* + +* **[octree]** Implementation of the iterator 'OctreeLeafNodeBreadthIterator'. [[#2204]](https://github.com/PointCloudLibrary/pcl/pull/2204) +* **[common][segmentation]** Provide proper EuclideanClusterComparator method depreciation. New Pragma macro. New Deprecated type. [[#2096]](https://github.com/PointCloudLibrary/pcl/pull/2096) +* **[io]** Add support pcl::PointXYZRGBA to pcl::VLPGrabber. Deprecate rgb signatures. [[#2102]](https://github.com/PointCloudLibrary/pcl/pull/2102) +* **[surface][tools]** Add new mls projection method. Deprecated `MovingLeastSquares::setPolynomialFit()`. [[#1960]](https://github.com/PointCloudLibrary/pcl/pull/1960) + +### `Removed:` + +*Removal of deprecated code.* + +* **[filters][io][surface][visualization]** Removal of deprecated code in filters, io, surface and visualization modules [[#2077]](https://github.com/PointCloudLibrary/pcl/pull/2077) +* **[common]** Remove deprecated ros headers [[#2075]](https://github.com/PointCloudLibrary/pcl/pull/2075) +* **[registration]** Remove registration module deprecated methods [[#2076]](https://github.com/PointCloudLibrary/pcl/pull/2076) +* **[sample_consensus]** Remove deprecated functions and variables from SAC module [[#2071]](https://github.com/PointCloudLibrary/pcl/pull/2071) +* **[common]** Removal of PCA deprecated constructor [[#2070]](https://github.com/PointCloudLibrary/pcl/pull/2070) + +### `Behavioral changes:` + +*Changes in the expected default behavior.* + +* **[common]** PointCloudDepthAndRGBtoXYZRGBA: initialize with the default alpha value (fix #2476) [[#2533]](https://github.com/PointCloudLibrary/pcl/pull/2533) +* **[octree]** Reverse octree's depth first iterator order [[#2332]](https://github.com/PointCloudLibrary/pcl/pull/2332) +* **[common]** `PointXYZRGBL` `label` field is now default constructed to 0 [[#2462]](https://github.com/PointCloudLibrary/pcl/pull/2462) +* **[io]** Fix PLYReader is_dense behavior [[#2133]](https://github.com/PointCloudLibrary/pcl/pull/2133) + +### `API changes:` + +*Changes to the API which didn't went through the proper deprecation and removal cycle.* + +* **[octree]** Implementation of the iterator 'OctreeLeafNodeBreadthIterator'. [[#2204]](https://github.com/PointCloudLibrary/pcl/pull/2204) +* **[sample_consensus]** Const-qualify most of the methods in SAC model classes [[#2270]](https://github.com/PointCloudLibrary/pcl/pull/2270) +* **[simulation]** Use GLuint rather than size_t to represent OpenGL indices. [[#2238]](https://github.com/PointCloudLibrary/pcl/pull/2238) +* **[visualization]** Fix access specifier in `PointCloudColorHandlerRGBAField` [[#2226]](https://github.com/PointCloudLibrary/pcl/pull/2226) +* **[docs]** Misc. typos (cont.) [[#2215]](https://github.com/PointCloudLibrary/pcl/pull/2215) +* **[octree]** OctreeIterators special member revision [[#2108]](https://github.com/PointCloudLibrary/pcl/pull/2108) +* **[io]** Add support pcl::PointXYZRGBA to pcl::VLPGrabber. Deprecate rgb signatures. [[#2102]](https://github.com/PointCloudLibrary/pcl/pull/2102) +* **[surface][tools]** Add new mls projection method. Deprecated `MovingLeastSquares::setPolynomialFit()`. [[#1960]](https://github.com/PointCloudLibrary/pcl/pull/1960) +* **[surface]** Add ability to cache mls results [[#1952]](https://github.com/PointCloudLibrary/pcl/pull/1952) + +### `ABI changes:` + +*Changes that cause ABI incompatibility but are still API compatible.* + +* **[surface]** Missing pcl::MovingLeastSquaresOMP declaration without /openmp [[#2324]](https://github.com/PointCloudLibrary/pcl/pull/2324) +* **[common][filters][surface]** Improved docstrings and error messages [[#2300]](https://github.com/PointCloudLibrary/pcl/pull/2300) +* **[common]** Modified `GlasbeyLUT` indexing type to `size_t` [[#2297]](https://github.com/PointCloudLibrary/pcl/pull/2297) +* **[octree]** Implementation of the iterator 'OctreeFixedDepthIterator'. [[#1983]](https://github.com/PointCloudLibrary/pcl/pull/1983) +* **[common][segmentation]** Provide proper EuclideanClusterComparator method depreciation. New Pragma macro. New Deprecated type. [[#2096]](https://github.com/PointCloudLibrary/pcl/pull/2096) +* **[gpu]** Allow specifying decimation step in convertToTsdfCloud [[#2099]](https://github.com/PointCloudLibrary/pcl/pull/2099) +* **[apps]** More warning suppression in pcl apps [[#2080]](https://github.com/PointCloudLibrary/pcl/pull/2080) +* **[io]** Removed unused member from ply_parser [[#2066]](https://github.com/PointCloudLibrary/pcl/pull/2066) +* **[filters]** Fixes remove_indices in UniformSampling [[#1902]](https://github.com/PointCloudLibrary/pcl/pull/1902) +* **[visualization]** Add accessor for current rendering framerate in PCLVisualizer [[#1974]](https://github.com/PointCloudLibrary/pcl/pull/1974) +* **[simulation]** Redo: Simulation: enable returning of organized point clouds [[#1687]](https://github.com/PointCloudLibrary/pcl/pull/1687) +* **[registration]** Added option to specify translation and rotation convergence deltas in ICP and NDT algorithms. [[#1724]](https://github.com/PointCloudLibrary/pcl/pull/1724) + +### `Modules:` + +#### `Uncategorized:` + +* Change Log generation tool. Automates change log generation. [[#2396]](https://github.com/PointCloudLibrary/pcl/pull/2396) +* Compatibility reports generation script [[#2410]](https://github.com/PointCloudLibrary/pcl/pull/2410) +* Update logo [[#2547]](https://github.com/PointCloudLibrary/pcl/pull/2547) +* Never close stale issues/prs [[#2400]](https://github.com/PointCloudLibrary/pcl/pull/2400) +* Fix typos in the whole codebase [[#2217]](https://github.com/PointCloudLibrary/pcl/pull/2217) +* Fixed typo and rearragend items in the issue template [[#2197]](https://github.com/PointCloudLibrary/pcl/pull/2197) +* Change Stale daysTillClose to 100 years [[#2166]](https://github.com/PointCloudLibrary/pcl/pull/2166) +* set stale daysUntilClose to a really big number [[#2162]](https://github.com/PointCloudLibrary/pcl/pull/2162) +* Stale set up [[#2101]](https://github.com/PointCloudLibrary/pcl/pull/2101) + +#### `CMake:` + +* Fix checks for user-provided CXX flags [[#2579]](https://github.com/PointCloudLibrary/pcl/pull/2579) +* Fix FLANN path to lower case [[#2576]](https://github.com/PointCloudLibrary/pcl/pull/2576) +* Use pkg-config to find Flann [[#2563]](https://github.com/PointCloudLibrary/pcl/pull/2563) +* Update FindBoost versions [[#2558]](https://github.com/PointCloudLibrary/pcl/pull/2558) +* Add PCL_BUILD_WITH_QHULL_DYNAMIC_LINKING_WIN32 option [[#2552]](https://github.com/PointCloudLibrary/pcl/pull/2552) +* Fix app/CMakeLists to enable Apps under Windows [[#2550]](https://github.com/PointCloudLibrary/pcl/pull/2550) +* When configuring with WITH_DOCS, but Doxygen is not available, prevent generation. [[#2516]](https://github.com/PointCloudLibrary/pcl/pull/2516) +* CMake: Do not include test targets in PCLConfig.cmake [[#2458]](https://github.com/PointCloudLibrary/pcl/pull/2458) +* CMake Set temporarily the policy CMP0074 to OLD [[#2454]](https://github.com/PointCloudLibrary/pcl/pull/2454) +* prevent GCC flags propagating to NVCC [[#2430]](https://github.com/PointCloudLibrary/pcl/pull/2430) +* Mark visualization as an optional dependency of tools [[#2439]](https://github.com/PointCloudLibrary/pcl/pull/2439) +* Do not mark imported libraries as GLOBAL in PCLConfig [[#2435]](https://github.com/PointCloudLibrary/pcl/pull/2435) +* Intel fixes [[#2432]](https://github.com/PointCloudLibrary/pcl/pull/2432) +* Export `-march=native` for Clang and prevent it from being included during cross compilation. [[#2416]](https://github.com/PointCloudLibrary/pcl/pull/2416) +* Do not search for PCL components that have been found already [[#2428]](https://github.com/PointCloudLibrary/pcl/pull/2428) +* Move SSE compiler options to `PCL_COMPILE_OPTIONS`. Expose PCL as a CMake imported target. [[#2100]](https://github.com/PointCloudLibrary/pcl/pull/2100) +* Add Visual Studio compiler option /FS for Ninja build [[#2414]](https://github.com/PointCloudLibrary/pcl/pull/2414) +* Use rpath in the target's install name [[#2241]](https://github.com/PointCloudLibrary/pcl/pull/2241) +* Improve QHull finder script [[#2344]](https://github.com/PointCloudLibrary/pcl/pull/2344) +* Fix link order issue with boost [[#2236]](https://github.com/PointCloudLibrary/pcl/pull/2236) +* Mark found PCL component libraries and include dirs as advanced [[#2235]](https://github.com/PointCloudLibrary/pcl/pull/2235) +* Prevent search for disabled optional dependencies in targets. [[#2229]](https://github.com/PointCloudLibrary/pcl/pull/2229) +* Fix installation rules for ml module [[#2192]](https://github.com/PointCloudLibrary/pcl/pull/2192) +* Fix conditional branch of Visual C++ 2017 [[#2121]](https://github.com/PointCloudLibrary/pcl/pull/2121) +* Add *_USE_STATIC options to PCLConfig [[#2086]](https://github.com/PointCloudLibrary/pcl/pull/2086) +* Add search path suffixes for Vcpkg [[#2085]](https://github.com/PointCloudLibrary/pcl/pull/2085) +* Update finder scripts for Ensenso, OpenNI, and OpenNI2 [[#2061]](https://github.com/PointCloudLibrary/pcl/pull/2061) +* Fix PACKAGE to include cmake/Modules directory [[#2053]](https://github.com/PointCloudLibrary/pcl/pull/2053) +* Unifies Find scripts in PCLConfig [[#1421]](https://github.com/PointCloudLibrary/pcl/pull/1421) +* CUDA 9 Arch Flags [[#2047]](https://github.com/PointCloudLibrary/pcl/pull/2047) +* Suppress log when PCL_FIND_QUIETLY is turned on. [[#2032]](https://github.com/PointCloudLibrary/pcl/pull/2032) +* fix /MP option not generated for Visual Studio. [[#2031]](https://github.com/PointCloudLibrary/pcl/pull/2031) +* Generate pkgconfig for 2d module [[#1979]](https://github.com/PointCloudLibrary/pcl/pull/1979) +* Update Find Boost [[#1972]](https://github.com/PointCloudLibrary/pcl/pull/1972) +* Added CUDA compute capability 5.3 [[#1929]](https://github.com/PointCloudLibrary/pcl/pull/1929) +* Fix issue with finding pcl deployed out of path [[#1923]](https://github.com/PointCloudLibrary/pcl/pull/1923) +* Add new gtest path [[#1920]](https://github.com/PointCloudLibrary/pcl/pull/1920) + +#### `libpcl_2d:` + +* Avoid huge index jumps in `RandomSample`. Remove `io` dependency from `2d`. [[#2141]](https://github.com/PointCloudLibrary/pcl/pull/2141) +* Fix header names [[#2079]](https://github.com/PointCloudLibrary/pcl/pull/2079) +* Generate pkgconfig for 2d module [[#1979]](https://github.com/PointCloudLibrary/pcl/pull/1979) + +#### `libpcl_common:` + +* Fix docstrings [[#2591]](https://github.com/PointCloudLibrary/pcl/pull/2591) +* Throw an early exception to prevent divide by zero error (#2481) [[#2530]](https://github.com/PointCloudLibrary/pcl/pull/2530) +* Relax requirements in eigen22d test. Always provide a normalized result in `pcl::transformPlane`. [[#2503]](https://github.com/PointCloudLibrary/pcl/pull/2503) +* **[behavior]** PointCloudDepthAndRGBtoXYZRGBA: initialize with the default alpha value (fix #2476) [[#2533]](https://github.com/PointCloudLibrary/pcl/pull/2533) +* Throw `UnorganizedPointCloudException` in `PointCloud::at` [[#2521]](https://github.com/PointCloudLibrary/pcl/pull/2521) +* Add missing const specifier for getters in `PCLBase`. [[#2502]](https://github.com/PointCloudLibrary/pcl/pull/2502) +* swap the header in pcl::PointCloud::swap [[#2499]](https://github.com/PointCloudLibrary/pcl/pull/2499) +* Add header guard and copyright info to polynomial_calculations.hpp [[#2500]](https://github.com/PointCloudLibrary/pcl/pull/2500) +* Add `header` to the print output of `PointCloud` [[#2498]](https://github.com/PointCloudLibrary/pcl/pull/2498) +* Fix force recalculation option in `BivariatePolynomialT::calculateGradient` [[#2479]](https://github.com/PointCloudLibrary/pcl/pull/2479) +* Fix various errors and typos in the docstrings and tutorials [[#2486]](https://github.com/PointCloudLibrary/pcl/pull/2486) +* Fix a bug in `PointRGBtoI` color conversion [[#2475]](https://github.com/PointCloudLibrary/pcl/pull/2475) +* Provide `operator<<` for `Intensity32u` point type [[#2467]](https://github.com/PointCloudLibrary/pcl/pull/2467) +* **[behavior]** `PointXYZRGBL` `label` field is now default constructed to 0 [[#2462]](https://github.com/PointCloudLibrary/pcl/pull/2462) +* Add some missing eigen alignment operators [[#2433]](https://github.com/PointCloudLibrary/pcl/pull/2433) +* Intel fixes [[#2432]](https://github.com/PointCloudLibrary/pcl/pull/2432) +* **[new-feature]** Add Viridis color LUT [[#2420]](https://github.com/PointCloudLibrary/pcl/pull/2420) +* Remove malloc header to restore builds on BSDs [[#2374]](https://github.com/PointCloudLibrary/pcl/pull/2374) +* Add support for multiple extensions in `parse_file_extension_argument ()`. [[#2347]](https://github.com/PointCloudLibrary/pcl/pull/2347) +* Improve speed of `transformPointCloud/WithNormals()` functions [[#2247]](https://github.com/PointCloudLibrary/pcl/pull/2247) +* Add RGB constructor that takes R, G, and B components [[#2329]](https://github.com/PointCloudLibrary/pcl/pull/2329) +* **[abi]** Improved docstrings and error messages [[#2300]](https://github.com/PointCloudLibrary/pcl/pull/2300) +* **[abi]** Modified `GlasbeyLUT` indexing type to `size_t` [[#2297]](https://github.com/PointCloudLibrary/pcl/pull/2297) +* Add GASDSignatures to `PCL_POINT_TYPES` and `PCL_FEATURE_POINTTYPES` macros. [[#2295]](https://github.com/PointCloudLibrary/pcl/pull/2295) +* [PARSE] Constness of the API [[#2224]](https://github.com/PointCloudLibrary/pcl/pull/2224) +* Fix two "unreachable code" warnings in `pca.hpp` [[#2219]](https://github.com/PointCloudLibrary/pcl/pull/2219) +* Fix covariance calculation in PCA [[#2130]](https://github.com/PointCloudLibrary/pcl/pull/2130) +* **[abi][deprecation]** Provide proper EuclideanClusterComparator method depreciation. New Pragma macro. New Deprecated type. [[#2096]](https://github.com/PointCloudLibrary/pcl/pull/2096) +* **[removal]** Remove deprecated ros headers [[#2075]](https://github.com/PointCloudLibrary/pcl/pull/2075) +* Suppress (maybe) uninitialized warning [[#2073]](https://github.com/PointCloudLibrary/pcl/pull/2073) +* **[removal]** Removal of PCA deprecated constructor [[#2070]](https://github.com/PointCloudLibrary/pcl/pull/2070) +* [gcc] fixes -Wimplicit-fallthrough: common/io.h [[#2041]](https://github.com/PointCloudLibrary/pcl/pull/2041) +* Include pcl/point_cloud.h and pcl/point_types.h headers. [[#1962]](https://github.com/PointCloudLibrary/pcl/pull/1962) +* Add test for macro _USE_MATH_DEFINES. [[#1956]](https://github.com/PointCloudLibrary/pcl/pull/1956) +* instantiate: remove duplicate macro definition. Fixes #1924. [[#1925]](https://github.com/PointCloudLibrary/pcl/pull/1925) + +#### `libpcl_cuda:` + +* add support for latest Turing gpu and cuda 10 [[#2560]](https://github.com/PointCloudLibrary/pcl/pull/2560) +* Fix compilation issues with CUDA 9.1 [[#2212]](https://github.com/PointCloudLibrary/pcl/pull/2212) +* Fix some CUDA 9 related errors [[#2181]](https://github.com/PointCloudLibrary/pcl/pull/2181) +* Added CUDA compute capability 5.3 [[#1929]](https://github.com/PointCloudLibrary/pcl/pull/1929) + +#### `libpcl_features:` + +* Solve issues with failing features tests [[#2544]](https://github.com/PointCloudLibrary/pcl/pull/2544) +* Update the OpenMP implementations of normal and FPFH estimation [[#2278]](https://github.com/PointCloudLibrary/pcl/pull/2278) +* Make `MomentOfInertia` instantiations consistent with the rest of the library [[#2266]](https://github.com/PointCloudLibrary/pcl/pull/2266) +* Docstring corrections [[#2143]](https://github.com/PointCloudLibrary/pcl/pull/2143) +* Improve Doxygen comments for HistogramInterpolationMethod [[#2111]](https://github.com/PointCloudLibrary/pcl/pull/2111) +* **[new-feature]** Add GASD global point cloud descriptor [[#1652]](https://github.com/PointCloudLibrary/pcl/pull/1652) +* Suppress (maybe) uninitialized warning [[#2073]](https://github.com/PointCloudLibrary/pcl/pull/2073) +* **[new-feature]** FLARELocalReferenceFrameEstimation class added [[#1571]](https://github.com/PointCloudLibrary/pcl/pull/1571) +* fix missing include file: from_meshes.h is using pcl::Vertices in it [[#2009]](https://github.com/PointCloudLibrary/pcl/pull/2009) +* Typo [[#1968]](https://github.com/PointCloudLibrary/pcl/pull/1968) + +#### `libpcl_filters:` + +* Corrections to CovarianceSampling class and corresponding test [[#2512]](https://github.com/PointCloudLibrary/pcl/pull/2512) +* Add the missing const modifier in `Filter::getRemovedIndices`. [[#2523]](https://github.com/PointCloudLibrary/pcl/pull/2523) +* Add const modifiers to getters of pcl::PassThrough [[#2524]](https://github.com/PointCloudLibrary/pcl/pull/2524) +* Add const specifiers for getters in VoxelGrid. [[#2526]](https://github.com/PointCloudLibrary/pcl/pull/2526) +* Copy the pose info from the input cloud to the output cloud in NaN removal functions [[#2522]](https://github.com/PointCloudLibrary/pcl/pull/2522) +* Fix misc. typos in tutorials and docstrings [[#2529]](https://github.com/PointCloudLibrary/pcl/pull/2529) +* Fix various errors and typos in the docstrings and tutorials [[#2486]](https://github.com/PointCloudLibrary/pcl/pull/2486) +* Add some missing eigen alignment operators [[#2433]](https://github.com/PointCloudLibrary/pcl/pull/2433) +* Add PointNormal to ExtractIndices Instantiate Types [[#2389]](https://github.com/PointCloudLibrary/pcl/pull/2389) +* **[abi]** Improved docstrings and error messages [[#2300]](https://github.com/PointCloudLibrary/pcl/pull/2300) +* Public access to `VoxelGrid` boost pointer. [[#2205]](https://github.com/PointCloudLibrary/pcl/pull/2205) +* Add const qualifiers to getters in `filter_indices.h` [[#2193]](https://github.com/PointCloudLibrary/pcl/pull/2193) +* Avoid huge index jumps in `RandomSample`. Remove `io` dependency from `2d`. [[#2141]](https://github.com/PointCloudLibrary/pcl/pull/2141) +* **[removal]** Removal of deprecated code in filters, io, surface and visualization modules [[#2077]](https://github.com/PointCloudLibrary/pcl/pull/2077) +* Suppress unused parameter warning [[#2074]](https://github.com/PointCloudLibrary/pcl/pull/2074) +* Suppress sign compare warnings [[#2068]](https://github.com/PointCloudLibrary/pcl/pull/2068) +* Transformation Fix for BoxClipper3D [[#1961]](https://github.com/PointCloudLibrary/pcl/pull/1961) +* **[abi]** Fixes remove_indices in UniformSampling [[#1902]](https://github.com/PointCloudLibrary/pcl/pull/1902) +* Inherit StatisticalOutlierRemoval from FilterIndices [[#1663]](https://github.com/PointCloudLibrary/pcl/pull/1663) + +#### `libpcl_gpu:` + +* Remove sm_72 from CUDA 9.0 [[#2567]](https://github.com/PointCloudLibrary/pcl/pull/2567) +* Fix compilation issues with CUDA 9.1 [[#2212]](https://github.com/PointCloudLibrary/pcl/pull/2212) +* Fix compilation error in `gpu_people` code [[#2199]](https://github.com/PointCloudLibrary/pcl/pull/2199) +* Fix some CUDA 9 related errors [[#2181]](https://github.com/PointCloudLibrary/pcl/pull/2181) +* **[abi]** Allow specifying decimation step in convertToTsdfCloud [[#2099]](https://github.com/PointCloudLibrary/pcl/pull/2099) +* Fix the incorrect include directory. [[#2024]](https://github.com/PointCloudLibrary/pcl/pull/2024) +* need to include instantiate.hpp to use PCL_INSTANTIATE [[#1943]](https://github.com/PointCloudLibrary/pcl/pull/1943) +* Added CUDA compute capability 5.3 [[#1929]](https://github.com/PointCloudLibrary/pcl/pull/1929) +* Fix issue #1674 [[#1926]](https://github.com/PointCloudLibrary/pcl/pull/1926) + +#### `libpcl_io:` + +* Suppress miscelanious warnings [[#2556]](https://github.com/PointCloudLibrary/pcl/pull/2556) +* vtk2mesh: Add parsing support to the new RGBA scalar field added in vtk8 [[#2492]](https://github.com/PointCloudLibrary/pcl/pull/2492) +* Fix various errors and typos in the docstrings and tutorials [[#2486]](https://github.com/PointCloudLibrary/pcl/pull/2486) +* Improved obj file parsing efficiency. Make parsing robust against situations where there are more normals than points. Added unit tests. [[#2450]](https://github.com/PointCloudLibrary/pcl/pull/2450) +* `pcl::PCDReader::readHeader()` change `nr_points` type to `size_t` to avoid possible `int32` overflow [[#2408]](https://github.com/PointCloudLibrary/pcl/pull/2408) +* Fix raw_fallocate for Android and deal with unsupported filesystems. [[#2363]](https://github.com/PointCloudLibrary/pcl/pull/2363) +* Add low_level_io.h to the header list of the io module [[#2356]](https://github.com/PointCloudLibrary/pcl/pull/2356) +* Created header for low level I/O helpers. Fix for `::posix_fallocate` on Mac OSX [[#2354]](https://github.com/PointCloudLibrary/pcl/pull/2354) +* Added warnings when the input data is too large for compressed pcds [[#2323]](https://github.com/PointCloudLibrary/pcl/pull/2323) +* Allocate disk space with posix_fallocate before mmapping. [[#2325]](https://github.com/PointCloudLibrary/pcl/pull/2325) +* Fix cmake warning: Logical block closes with mis-matching arguments [[#2320]](https://github.com/PointCloudLibrary/pcl/pull/2320) +* Added PCL_IO_ENABLE_MAND_LOCKING cmake flag. [[#2315]](https://github.com/PointCloudLibrary/pcl/pull/2315) +* Added missing 8 bytes to compressed binary pcd length. [[#2281]](https://github.com/PointCloudLibrary/pcl/pull/2281) +* Remove useless size check in PLYReader::endHeaderCallback() [[#2246]](https://github.com/PointCloudLibrary/pcl/pull/2246) +* **[behavior]** Fix PLYReader is_dense behavior [[#2133]](https://github.com/PointCloudLibrary/pcl/pull/2133) +* `EnsensoGrabber` `uint` is undefined in Visual studio. [[#2223]](https://github.com/PointCloudLibrary/pcl/pull/2223) +* Add protection from invalid WIDTH values in PCD reader [[#2195]](https://github.com/PointCloudLibrary/pcl/pull/2195) +* `PLYReader` Cast cloud point step as 64-bit integer [[#2161]](https://github.com/PointCloudLibrary/pcl/pull/2161) +* `OpenNI2Device` Add device sensor check for IR and depth modesetting [[#2150]](https://github.com/PointCloudLibrary/pcl/pull/2150) +* Adds a check for when CreateFileMappingA fails [[#2146]](https://github.com/PointCloudLibrary/pcl/pull/2146) +* `PCDWriter`changed `toff` to `size_t` in `writeBinaryCompressed` [[#2144]](https://github.com/PointCloudLibrary/pcl/pull/2144) +* Prevent POINTS field parsing before point_step is specified [[#2131]](https://github.com/PointCloudLibrary/pcl/pull/2131) +* Check COUNT value specified in PCD files [[#2126]](https://github.com/PointCloudLibrary/pcl/pull/2126) +* Prevent mmapping more than the original PCD file size [[#2125]](https://github.com/PointCloudLibrary/pcl/pull/2125) +* **[api][deprecation]** Add support pcl::PointXYZRGBA to pcl::VLPGrabber. Deprecate rgb signatures. [[#2102]](https://github.com/PointCloudLibrary/pcl/pull/2102) +* **[removal]** Removal of deprecated code in filters, io, surface and visualization modules [[#2077]](https://github.com/PointCloudLibrary/pcl/pull/2077) +* Suppress strict alias warning [[#2072]](https://github.com/PointCloudLibrary/pcl/pull/2072) +* Suppress unused parameter warnings [[#2067]](https://github.com/PointCloudLibrary/pcl/pull/2067) +* **[abi]** Removed unused member from ply_parser [[#2066]](https://github.com/PointCloudLibrary/pcl/pull/2066) +* Suppress control reaches end of non-void function in io.h [[#2057]](https://github.com/PointCloudLibrary/pcl/pull/2057) +* Modify STRICT_ALIGN because macro expansion w/defined is undefined [[#2043]](https://github.com/PointCloudLibrary/pcl/pull/2043) +* Add necessary boost headers to pcl/io to build in CUDA mode [[#2025]](https://github.com/PointCloudLibrary/pcl/pull/2025) +* Fix MSVC compile issue related with ssize_t [[#2027]](https://github.com/PointCloudLibrary/pcl/pull/2027) +* Adds in-memory PCD serialization/deserialization; de-duplicates PCDReader::readHeader(). (take #2) [[#1986]](https://github.com/PointCloudLibrary/pcl/pull/1986) + +#### `libpcl_kdtree:` + +* Consistent ptr typedefs for kd tree flann [[#1607]](https://github.com/PointCloudLibrary/pcl/pull/1607) + +#### `libpcl_keypoints:` + +* Add `TrajkovicKeypoint2D/3D` to CMake build [[#2179]](https://github.com/PointCloudLibrary/pcl/pull/2179) + +#### `libpcl_ml:` + +* Fix installation rules for ml module [[#2192]](https://github.com/PointCloudLibrary/pcl/pull/2192) + +#### `libpcl_octree:` + +* **[behavior]** Reverse octree's depth first iterator order [[#2332]](https://github.com/PointCloudLibrary/pcl/pull/2332) +* Fix various errors and typos in the docstrings and tutorials [[#2486]](https://github.com/PointCloudLibrary/pcl/pull/2486) +* Make test conditions consistent with `OctreePointCloudSearch::boxSearch()` implementation. [[#2457]](https://github.com/PointCloudLibrary/pcl/pull/2457) +* `octree_key.h` suppress GCC 8 ignored-qualifier warning [[#2405]](https://github.com/PointCloudLibrary/pcl/pull/2405) +* **[api][deprecation][new-feature]** Implementation of the iterator 'OctreeLeafNodeBreadthIterator'. [[#2204]](https://github.com/PointCloudLibrary/pcl/pull/2204) +* **[abi][new-feature]** Implementation of the iterator 'OctreeFixedDepthIterator'. [[#1983]](https://github.com/PointCloudLibrary/pcl/pull/1983) +* Octree Iterator begin/end method and added tests [[#2174]](https://github.com/PointCloudLibrary/pcl/pull/2174) +* Remove parametrization of end iterators [[#2168]](https://github.com/PointCloudLibrary/pcl/pull/2168) +* Fix docstrings in octree test [[#2173]](https://github.com/PointCloudLibrary/pcl/pull/2173) +* **[api]** OctreeIterators special member revision [[#2108]](https://github.com/PointCloudLibrary/pcl/pull/2108) +* Remove unused variable from octree_viewer [[#2069]](https://github.com/PointCloudLibrary/pcl/pull/2069) +* Silence compile warning by removing superfluous call to std::max() [[#2014]](https://github.com/PointCloudLibrary/pcl/pull/2014) +* [OCTREE] Add bounding box checks in isVoxelOccupiedAtPoint() and deleteVoxelAtPoint() [[#1976]](https://github.com/PointCloudLibrary/pcl/pull/1976) + +#### `libpcl_outofcore:` + +* Explictly use mt19937 random generator for boost 1.67. [[#2338]](https://github.com/PointCloudLibrary/pcl/pull/2338) +* Fixed queryBBIncludes_subsample [[#1988]](https://github.com/PointCloudLibrary/pcl/pull/1988) + +#### `libpcl_people:` + +* Misleading indentation [[#2034]](https://github.com/PointCloudLibrary/pcl/pull/2034) + +#### `libpcl_recognition:` + +* Relax threshold in Hough3DGrouping test [[#2507]](https://github.com/PointCloudLibrary/pcl/pull/2507) +* Add some missing eigen alignment operators [[#2433]](https://github.com/PointCloudLibrary/pcl/pull/2433) +* Setting the resolution of the occupancy grid [[#2273]](https://github.com/PointCloudLibrary/pcl/pull/2273) +* Inline helper function gcCorrespSorter() [[#2248]](https://github.com/PointCloudLibrary/pcl/pull/2248) +* Misleading indentation [[#2034]](https://github.com/PointCloudLibrary/pcl/pull/2034) + +#### `libpcl_registration:` + +* Remove std::binary_function from Registration [[#2599]](https://github.com/PointCloudLibrary/pcl/pull/2599) +* Suppress miscelanious warnings [[#2556]](https://github.com/PointCloudLibrary/pcl/pull/2556) +* Relax precision requirements on TransformationEstimationLM test. [[#2497]](https://github.com/PointCloudLibrary/pcl/pull/2497) +* Relax rejector threshold in JointIterativeClosestPoint test. [[#2496]](https://github.com/PointCloudLibrary/pcl/pull/2496) +* Add some missing eigen alignment operators [[#2433]](https://github.com/PointCloudLibrary/pcl/pull/2433) +* Remove explicit initialization of `update_visualizer_` in `Registration` [[#2423]](https://github.com/PointCloudLibrary/pcl/pull/2423) +* **[removal]** Remove registration module deprecated methods [[#2076]](https://github.com/PointCloudLibrary/pcl/pull/2076) +* Suppress (maybe) uninitialized warning [[#2073]](https://github.com/PointCloudLibrary/pcl/pull/2073) +* Remove unreachable code in DefaultConvergenceCriteria [[#1967]](https://github.com/PointCloudLibrary/pcl/pull/1967) +* **[abi]** Added option to specify translation and rotation convergence deltas in ICP and NDT algorithms. [[#1724]](https://github.com/PointCloudLibrary/pcl/pull/1724) + +#### `libpcl_sample_consensus:` + +* Revise direction test in SampleConsensusModelParallelLine/RANSAC to be consistent with set tolerance. [[#2491]](https://github.com/PointCloudLibrary/pcl/pull/2491) +* Fix error in SampleConsensusModelLine::isSampleGood [[#2488]](https://github.com/PointCloudLibrary/pcl/pull/2488) +* **[api]** Const-qualify most of the methods in SAC model classes [[#2270]](https://github.com/PointCloudLibrary/pcl/pull/2270) +* **[removal]** Remove deprecated functions and variables from SAC module [[#2071]](https://github.com/PointCloudLibrary/pcl/pull/2071) + +#### `libpcl_search:` + +* Correct testPoint for organized nearest neighbor search [[#2198]](https://github.com/PointCloudLibrary/pcl/pull/2198) + +#### `libpcl_segmentation:` + +* Add some missing eigen alignment operators [[#2433]](https://github.com/PointCloudLibrary/pcl/pull/2433) +* Add setter/getter for search method in ConditionalEuclideanClustering [[#2437]](https://github.com/PointCloudLibrary/pcl/pull/2437) +* Increase threshold for expected value in test_non_linear [[#2424]](https://github.com/PointCloudLibrary/pcl/pull/2424) +* Avoid infinite recursion in getPointCloudDifference [[#2402]](https://github.com/PointCloudLibrary/pcl/pull/2402) +* Correct setting of is_dense flag in SegmentDifferences. Deprecate unused parameter in method. [[#2380]](https://github.com/PointCloudLibrary/pcl/pull/2380) +* Dereference shared_ptr, fix for GCC8 [[#2299]](https://github.com/PointCloudLibrary/pcl/pull/2299) +* **[abi][deprecation]** Provide proper EuclideanClusterComparator method depreciation. New Pragma macro. New Deprecated type. [[#2096]](https://github.com/PointCloudLibrary/pcl/pull/2096) +* Removed normal related accessors and types from EuclideanClusterComparator [[#1542]](https://github.com/PointCloudLibrary/pcl/pull/1542) + +#### `libpcl_simulation:` + +* Add `const` qualifiers to `RangeLikelihood` getters. [[#2411]](https://github.com/PointCloudLibrary/pcl/pull/2411) +* **[api]** Use GLuint rather than size_t to represent OpenGL indices. [[#2238]](https://github.com/PointCloudLibrary/pcl/pull/2238) +* Support both RGB and RGBA colors in mesh loading [[#2036]](https://github.com/PointCloudLibrary/pcl/pull/2036) +* **[abi]** Redo: Simulation: enable returning of organized point clouds [[#1687]](https://github.com/PointCloudLibrary/pcl/pull/1687) +* Simulation: more access to camera parameters [[#1650]](https://github.com/PointCloudLibrary/pcl/pull/1650) + +#### `libpcl_surface:` + +* Fixed memory leak in Poisson's BSplineData [[#2572]](https://github.com/PointCloudLibrary/pcl/pull/2572) +* Suppress miscelanious warnings [[#2556]](https://github.com/PointCloudLibrary/pcl/pull/2556) +* Add some missing eigen alignment operators [[#2433]](https://github.com/PointCloudLibrary/pcl/pull/2433) +* Make pcl::MovingLeastSquares thread-safe [[#2418]](https://github.com/PointCloudLibrary/pcl/pull/2418) +* **[abi]** Missing pcl::MovingLeastSquaresOMP declaration without /openmp [[#2324]](https://github.com/PointCloudLibrary/pcl/pull/2324) +* **[abi]** Improved docstrings and error messages [[#2300]](https://github.com/PointCloudLibrary/pcl/pull/2300) +* opennurbs: fix `ON_Curve::EvaluatePoint` calculation [[#2185]](https://github.com/PointCloudLibrary/pcl/pull/2185) +* **[removal]** Removal of deprecated code in filters, io, surface and visualization modules [[#2077]](https://github.com/PointCloudLibrary/pcl/pull/2077) +* Suppress (maybe) uninitialized warning [[#2073]](https://github.com/PointCloudLibrary/pcl/pull/2073) +* Suppress sign compare warnings [[#2068]](https://github.com/PointCloudLibrary/pcl/pull/2068) +* Fix incorrect Ptr/ConstPtr typedefs in MovingLeastSquaresOMP [[#2055]](https://github.com/PointCloudLibrary/pcl/pull/2055) +* Replace float indices with Eigen Index [[#2017]](https://github.com/PointCloudLibrary/pcl/pull/2017) +* **[api][deprecation][new-feature]** Add new mls projection method. Deprecated `MovingLeastSquares::setPolynomialFit()`. [[#1960]](https://github.com/PointCloudLibrary/pcl/pull/1960) +* Avoid phantom surfces in marching cubes hoppe [[#1874]](https://github.com/PointCloudLibrary/pcl/pull/1874) +* **[api]** Add ability to cache mls results [[#1952]](https://github.com/PointCloudLibrary/pcl/pull/1952) + +#### `PCL Apps:` + +* Suppress miscelanious warnings [[#2556]](https://github.com/PointCloudLibrary/pcl/pull/2556) +* Fix 3d_rec_framework compilation error [[#2495]](https://github.com/PointCloudLibrary/pcl/pull/2495) +* Fix compilation issue in point cloud editor. [[#2490]](https://github.com/PointCloudLibrary/pcl/pull/2490) +* `demean_cloud` correct usage message. [[#2443]](https://github.com/PointCloudLibrary/pcl/pull/2443) +* Do not use deprecated method in openni_mls_smoothing app [[#2421]](https://github.com/PointCloudLibrary/pcl/pull/2421) +* add windows.h for includes GL/gl.h; handle cancel for denoiseWidget. [[#2267]](https://github.com/PointCloudLibrary/pcl/pull/2267) +* Add missing dependecy to apps [[#2251]](https://github.com/PointCloudLibrary/pcl/pull/2251) +* Suppress the final set of warnings in pcl apps [[#2082]](https://github.com/PointCloudLibrary/pcl/pull/2082) +* **[abi]** More warning suppression in pcl apps [[#2080]](https://github.com/PointCloudLibrary/pcl/pull/2080) + +#### `PCL Docs:` + +* Fix misc. typos in tutorials and docstrings [[#2529]](https://github.com/PointCloudLibrary/pcl/pull/2529) +* Fix various errors and typos in the docstrings and tutorials [[#2486]](https://github.com/PointCloudLibrary/pcl/pull/2486) +* Docstring typos' corrections. [[#2449]](https://github.com/PointCloudLibrary/pcl/pull/2449) +* `demean_cloud` correct usage message. [[#2443]](https://github.com/PointCloudLibrary/pcl/pull/2443) +* Set IMAGE_PATH explicitly in Doxygen config [[#2442]](https://github.com/PointCloudLibrary/pcl/pull/2442) +* Switch to using client-based search in Doxygen [[#2391]](https://github.com/PointCloudLibrary/pcl/pull/2391) +* **[api]** Misc. typos (cont.) [[#2215]](https://github.com/PointCloudLibrary/pcl/pull/2215) +* doc: misc. typos [[#2213]](https://github.com/PointCloudLibrary/pcl/pull/2213) +* Add url to API/ABI compatibity report [[#2116]](https://github.com/PointCloudLibrary/pcl/pull/2116) +* Improve Doxygen comments for HistogramInterpolationMethod [[#2111]](https://github.com/PointCloudLibrary/pcl/pull/2111) +* Update organized.h [[#1965]](https://github.com/PointCloudLibrary/pcl/pull/1965) +* Typo [[#1968]](https://github.com/PointCloudLibrary/pcl/pull/1968) +* Fixed spelling and grammar errors [[#1959]](https://github.com/PointCloudLibrary/pcl/pull/1959) +* Fixed error in documentation. [[#1957]](https://github.com/PointCloudLibrary/pcl/pull/1957) + +#### `PCL Tutorials:` + +* Fix dataset link in conditional euclidean clustering tutorial [[#2546]](https://github.com/PointCloudLibrary/pcl/pull/2546) +* Fix dead links in Walkthrough tutorial [[#2532]](https://github.com/PointCloudLibrary/pcl/pull/2532) +* Simplify explanation of PointXYZ structure in "Writing PCD" tutorial [[#2534]](https://github.com/PointCloudLibrary/pcl/pull/2534) +* Fix misc. typos in tutorials and docstrings [[#2529]](https://github.com/PointCloudLibrary/pcl/pull/2529) +* Fix a dead link to Radu Rusu's dissertation in the tutorial. [[#2508]](https://github.com/PointCloudLibrary/pcl/pull/2508) +* Fix various errors and typos in the docstrings and tutorials [[#2486]](https://github.com/PointCloudLibrary/pcl/pull/2486) +* Fix link to Institut Maupertuis's ensenso_extrinsic_calibration repo [[#2447]](https://github.com/PointCloudLibrary/pcl/pull/2447) +* Add settings for hypothesis verification [[#2274]](https://github.com/PointCloudLibrary/pcl/pull/2274) +* Fix ICP tutorial [[#2244]](https://github.com/PointCloudLibrary/pcl/pull/2244) +* Fix error in example code for estimate set of surface for a subset of points in the input dataset [[#2203]](https://github.com/PointCloudLibrary/pcl/pull/2203) +* Update message about legacy point cloud types in tutorial [[#2175]](https://github.com/PointCloudLibrary/pcl/pull/2175) +* Add descriptor unpacking to GASD tutorial [[#2167]](https://github.com/PointCloudLibrary/pcl/pull/2167) +* Fix convert to `Eigen::Map` from Normal of `pcl::PointXYZINormal` [[#2128]](https://github.com/PointCloudLibrary/pcl/pull/2128) +* Fix the tutorial qt_visualizer compilation issue: qt4 -> qt5. [[#2051]](https://github.com/PointCloudLibrary/pcl/pull/2051) +* Fix several documentation typos [[#2020]](https://github.com/PointCloudLibrary/pcl/pull/2020) +* Replace literal include of wrong CMakeLists file with correct script [[#1971]](https://github.com/PointCloudLibrary/pcl/pull/1971) +* Update Ensenso tutorial for Ensenso X devices [[#1933]](https://github.com/PointCloudLibrary/pcl/pull/1933) + +#### `PCL Examples:` + +* Suppress strict alias warning [[#2072]](https://github.com/PointCloudLibrary/pcl/pull/2072) +* Suppress (maybe) uninitialized warning [[#2073]](https://github.com/PointCloudLibrary/pcl/pull/2073) +* Fix CPC/LCCP segmentation examples for VTK 7.1 [[#2063]](https://github.com/PointCloudLibrary/pcl/pull/2063) + +#### `PCL Tests:` + +* Corrections to Windows unit tests. [[#2596]](https://github.com/PointCloudLibrary/pcl/pull/2596) +* Relax eigen22f test criteria [[#2553]](https://github.com/PointCloudLibrary/pcl/pull/2553) +* Solve issues with failing features tests [[#2544]](https://github.com/PointCloudLibrary/pcl/pull/2544) +* Relax requirements in eigen22d test. Always provide a normalized result in `pcl::transformPlane`. [[#2503]](https://github.com/PointCloudLibrary/pcl/pull/2503) +* Enable tests_2d and tests_io in AppVeyor. [[#2505]](https://github.com/PointCloudLibrary/pcl/pull/2505) +* Relax threshold in Hough3DGrouping test [[#2507]](https://github.com/PointCloudLibrary/pcl/pull/2507) +* Relax precision requirements on TransformationEstimationLM test. [[#2497]](https://github.com/PointCloudLibrary/pcl/pull/2497) +* Relax rejector threshold in JointIterativeClosestPoint test. [[#2496]](https://github.com/PointCloudLibrary/pcl/pull/2496) +* vtk2mesh: Add parsing support to the new RGBA scalar field added in vtk8 [[#2492]](https://github.com/PointCloudLibrary/pcl/pull/2492) +* Revise direction test in SampleConsensusModelParallelLine/RANSAC to be consistent with set tolerance. [[#2491]](https://github.com/PointCloudLibrary/pcl/pull/2491) +* Make test conditions consistent with `OctreePointCloudSearch::boxSearch()` implementation. [[#2457]](https://github.com/PointCloudLibrary/pcl/pull/2457) +* Increase threshold for expected value in test_non_linear [[#2424]](https://github.com/PointCloudLibrary/pcl/pull/2424) +* Replace floating point numerals when using `boost::posix_time`. Fix compatibility with Boost 1.67. [[#2422]](https://github.com/PointCloudLibrary/pcl/pull/2422) +* Cleanup and improve unit test coverage for transformPointCloud functions [[#2245]](https://github.com/PointCloudLibrary/pcl/pull/2245) +* Fixes and new assertion macro in "pcl_tests.h" [[#2237]](https://github.com/PointCloudLibrary/pcl/pull/2237) +* Add new gtest path [[#1920]](https://github.com/PointCloudLibrary/pcl/pull/1920) + +#### `PCL Tools:` + +* Allow the `pcl_uniform_sampling` tool to deal with several formats (PCD, PLY and VTK) as input or output point cloud [[#2348]](https://github.com/PointCloudLibrary/pcl/pull/2348) +* mesh_sampling tool: Add support for colors [[#2257]](https://github.com/PointCloudLibrary/pcl/pull/2257) +* Error message on non-recognized feature names [[#2178]](https://github.com/PointCloudLibrary/pcl/pull/2178) +* Suppress sign compare warnings [[#2068]](https://github.com/PointCloudLibrary/pcl/pull/2068) +* [OCTREE] Compute accurately the centroids of octree in 'pcl_octree_viewer' [[#1981]](https://github.com/PointCloudLibrary/pcl/pull/1981) +* **[api][deprecation][new-feature]** Add new mls projection method. Deprecated `MovingLeastSquares::setPolynomialFit()`. [[#1960]](https://github.com/PointCloudLibrary/pcl/pull/1960) +* [OCTREE] Fix pcl_octree_viewer [[#1973]](https://github.com/PointCloudLibrary/pcl/pull/1973) +* [OCTREE] Remove a useless field in octree_viewer. [[#1980]](https://github.com/PointCloudLibrary/pcl/pull/1980) + +#### `CI:` + +* Disable Travis email notifications. Update PCL logo endpoint. [[#2535]](https://github.com/PointCloudLibrary/pcl/pull/2535) +* Migrate Travis to the new travis-ci.com platform [[#2538]](https://github.com/PointCloudLibrary/pcl/pull/2538) +* Enable tests_2d and tests_io in AppVeyor. [[#2505]](https://github.com/PointCloudLibrary/pcl/pull/2505) +* Fix docs on Travis CI. [[#2441]](https://github.com/PointCloudLibrary/pcl/pull/2441) +* Disable SSE flags in AppVeyor. [[#2438]](https://github.com/PointCloudLibrary/pcl/pull/2438) +* Split (yet again) Travis test job into two and tweak timings in building apps [[#2182]](https://github.com/PointCloudLibrary/pcl/pull/2182) +* **[new-feature]** Enable Global Tests on Windows CI [[#2137]](https://github.com/PointCloudLibrary/pcl/pull/2137) +* Travis merge test jobs. Expose VTK include directory for the visualization module. [[#2163]](https://github.com/PointCloudLibrary/pcl/pull/2163) +* Remove unnecessary PPAs and packages from Travis [[#2153]](https://github.com/PointCloudLibrary/pcl/pull/2153) +* AppVeyor - Change to simple style of specify triplet [[#2135]](https://github.com/PointCloudLibrary/pcl/pull/2135) +* Initial Appveyor CI integration [[#2083]](https://github.com/PointCloudLibrary/pcl/pull/2083) +* Change Travis to use pip3 for installing sphinx [[#2124]](https://github.com/PointCloudLibrary/pcl/pull/2124) +* [TRAVIS] Enable the build of apps. [[#2012]](https://github.com/PointCloudLibrary/pcl/pull/2012) +* [TRAVIS] Enable the build of tools. [[#2007]](https://github.com/PointCloudLibrary/pcl/pull/2007) +* Disable tools build in CI. [[#2003]](https://github.com/PointCloudLibrary/pcl/pull/2003) + + ## *= 1.8.1 (08.08.2017) =* * Replaced `make_shared` invocations on aligned allocated vars @@ -384,7 +857,7 @@ * Removed unnecessary mutex locking in `TimeTrigger::registerCallback` [[#1087]](https://github.com/PointCloudLibrary/pcl/pull/1087) * Updated PCL exception types to have nothrow copy constructor and copy - assigment operator + assignment operator [[#1119]](https://github.com/PointCloudLibrary/pcl/pull/1119) * Fixed a bug with `PCA` not triggering recomputation when input indices are changed @@ -625,7 +1098,7 @@ * Added a new `MetaRegistration` class that allows to register a stream of clouds where each cloud is aligned to the conglomerate of all previous clouds [[#1426]](https://github.com/PointCloudLibrary/pcl/pull/1426) -* Fixed segmentation fault occuring in `CorrespondenceRejectorSurfaceNormal` +* Fixed segmentation fault occurring in `CorrespondenceRejectorSurfaceNormal` [[#1536]](https://github.com/PointCloudLibrary/pcl/pull/1536) * Use aligned allocator in vectors of MatchingCandidate [[#1552]](https://github.com/PointCloudLibrary/pcl/pull/1552) @@ -1381,13 +1854,13 @@ The most notable overall changes are: * Added a setDimension function to concave hull, so users can specify desired dimensionality of the resulting hull. If no dimension is specified, one will be automatically determined. * Fixed bug #692 - MovingLeastSquares indices issues * Added curve fitting and trimming of surfaces to examples/surface/example_nurbs_fitting_surface.cpp -* Added iterative fitting routines for curve fitting surface::on_nurbs::Triangulation - added convertion functions for nurbs curve to line-polygon - added convertion functions for nurbs surface and curve to PolyMesh +* Added iterative fitting routines for curve fitting surface::on_nurbs::Triangulation - added conversion functions for nurbs curve to line-polygon - added conversion functions for nurbs surface and curve to PolyMesh * Added flag to enable/disable usage of UmfPack for fast solving of sparse systems of equations - added triangulation functions to convert ON_NurbsSurface to pcl::PolygonMesh * Added bug fix in ConcaveHull, thanks to summer.icecream * Added marching cubes using RBF and Hoppe SDF * Pushed new functions that perform texture-mapping on meshes. * Fix: issue #646 (vtk_smoothing not copied) -* Added new functionalities to TextureMapping: Find occlusions based on raytracing in octrees, UV mapping based on Texture resolution and camera focal lenght. +* Added new functionalities to TextureMapping: Find occlusions based on raytracing in octrees, UV mapping based on Texture resolution and camera focal length. * Relaxing dimensionality check threshold on concave_hull, so that 3D hulls should no longer be calculated on 2D input. * Added poisson filter @@ -1629,7 +2102,7 @@ The most notable overall changes are: * added a normal space sampling filter * fixed bug #433: `pcl::CropBox` doesn't update `width` and `height` member of output point cloud * fixed bug #423 `CropBox` + `VoxelGrid` filters, order changes behaviour (using openni grabber) -* add `CropHull` filter for filtering points based on a 2D or 3D convex or concave hull. The ray-polygon intersection test is used, which relys on closed polygons/surfaces +* add `CropHull` filter for filtering points based on a 2D or 3D convex or concave hull. The ray-polygon intersection test is used, which relies on closed polygons/surfaces * added clipper3D interface and a draft plane_clipper3D implementation * removed spurious old `ColorFilter` class (obsolete, and it was never implemented - the skeleton was just lurking around) * better Doxygen documentation to `PassThrough`, `VoxelGrid` and `Filter` @@ -1683,7 +2156,7 @@ The most notable overall changes are: ### `lipcl_keypoints` * added refine method for `Harris3D` corner detector -* rewrote big parts of the NARF keypoint extraction. Hopfully fixing some stability issues. Unfortunately still pretty slow for high resolution point clouds. +* rewrote big parts of the NARF keypoint extraction. Hopefully fixing some stability issues. Unfortunately still pretty slow for high resolution point clouds. * fixed bug #461 (SIFT Keypoint result cloud fields not complete); cleaned up the line-wrapping in the error/warning messages ### `libpcl_surface` @@ -1827,7 +2300,7 @@ pcl::OrganizedDataIndex -> pcl::search::OrganizedNeighbor * fixed a bug in `getRejectedQueryIndices`, wrong output when order of correspondences have been changed * moved `getRejectedQueryIndices` to pcl/common/correspondence.h * added more doxygen documentation to the registration components -* marked all `getRemainingCorrespondences`-functions as DEPRECATED, we sould replace them with purely stateless version outside the class body +* marked all `getRemainingCorrespondences`-functions as DEPRECATED, we should replace them with purely stateless version outside the class body * fixed a const missing in `PolynomialCalculationsT` (#388 - thanks Julian!) * add `PCL_DEPRECATED` macro, closes #354. * added `PointXYZHSV` type and the conversions for it @@ -1913,11 +2386,11 @@ pcl::OrganizedDataIndex -> pcl::search::OrganizedNeighbor * fixed bug in getRejectedQueryIndices, wrong output when order of correspondences have been changed * moved getRejectedQueryIndices to pcl/common/correspondence.h * added more doxygen documentation to the registration components - * marked all "getRemainingCorrespondences"-functions as DEPRECATED, we sould replace them with purely stateless version outside the class body + * marked all "getRemainingCorrespondences"-functions as DEPRECATED, we should replace them with purely stateless version outside the class body * Update: remove ciminpack dependency and rely on eigen for LM * Fixed a bug in ICP-NL by modifying `WarpPointRigid` to preserve the value of the 4th coordinate when warping; Re-enabled missing unit tests for ICP and ICP-NL * Added point-to-plane ICP -* added nr_iterations_ and max_iterations_ to the initializer list (were mising) +* added nr_iterations_ and max_iterations_ to the initializer list (were missing) * Fixed bugs in `WarpPointRigid3D` and `TransformationEstimationLM` * fixed a problem where if no correspondences would be found via `nearestKSearch`, the RANSAC-based rejection scheme would fail (thanks to James for reporting this) * changed the default LM implementation to use L2 instead of L2SQR @@ -2306,7 +2779,7 @@ The version numbers below belong to the *perception_pcl* stack in ROS, which coo * added a new generalized field value filter (_pcl::ConditionalRemoval_) * cleaned up normal estimation through integral images * PCL rift.hpp and point_types.hpp fixed for Windows/VS2010 - * fixed all _is_dense_ occurances + * fixed all _is_dense_ occurrences * *unmanged all _Eigen3::_ calls to _Eigen::_* * changed all _!isnan_ checks to _isfinite_ in order to catch INF/-INF values * added vtk_io tools for saving VTK data from _PolygonMesh_ structures @@ -2341,7 +2814,7 @@ The version numbers below belong to the *perception_pcl* stack in ROS, which coo * Added Correspondence as a structure representing correspondences/matches (similar to OpenCV's DMatch) containing query and match indices as well as the distance between them respective points. * Added _CorrespondenceEstimation_ for determining closest point correspondence, feature correspondences and reciprocal point correspondences. * Added _CorrespondenceRejection_ and derivations for rejecting correspondences, e.g., based on distance, removing 1-to-n correspondences, RANSAC-based outlier removal (+transformation estimation). - * Further splitted up registration.h and added transformation estimation classes, e.g., for estimating rigid transformation using SVD. + * Further split up registration.h and added transformation estimation classes, e.g., for estimating rigid transformation using SVD. * Added ```sensor_msgs::Image image; pcl::toROSMsg (cloud, image);```See tools/convert_pcd_image.cpp for a sample. * Added a new point type, _PointWithScale_, to store the output of _SIFTKeypoint_ detection. * Fixed a minor bug in the error-checking in _SIFTKeypoint::setScales(...)_ @@ -2395,7 +2868,7 @@ The version numbers below belong to the *perception_pcl* stack in ROS, which coo * Added RSD (Radius Signature Descriptor) feature. * Updated the entire PCL tree to use FLANN as a default _KdTree_ * Optimized the _KdTreeFLANN::radiusSearch_ so it doesn't do any memory allocation if the indices and distances vectors passed in are pre-allocated to the point cloud size. - * Changed _KdTree::radiusSearch_ method signature to return int (number of neighbors found) intead of bool + * Changed _KdTree::radiusSearch_ method signature to return int (number of neighbors found) instead of bool * added new _pcl::View_ class that holds a _PointCloud_, an _Image_, and a _CameraInfo_ message (r34575) * Moving the _Surface_ reconstruction framework to the new structure (r34547) * Moving the _Segmentation_ framework to the new structure (r34546) diff --git a/CMakeLists.txt b/CMakeLists.txt index d36a581f..5b8ee5e5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,11 @@ if(POLICY CMP0020 AND (WIN32 AND NOT MINGW)) cmake_policy(SET CMP0020 NEW) # Automatically link Qt executables to qtmain target on Windows endif() +if(POLICY CMP0042) + # Uses Mac OS X @rpath in the target's install name + cmake_policy(SET CMP0042 NEW) +endif() + if(POLICY CMP0048) cmake_policy(SET CMP0048 OLD) # do not use VERSION option in project() command endif() @@ -19,12 +24,19 @@ if(POLICY CMP0054) cmake_policy(SET CMP0054 OLD) # Silent warnings about quoted variables endif() +if(POLICY CMP0074) + # TODO: + # 1. Find*.cmake modules need to be individually verified. + # 2. PCLConfig.cmake needs to be changed. + cmake_policy(SET CMP0074 OLD) +endif() + set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "possible configurations" FORCE) # In case the user does not setup CMAKE_BUILD_TYPE, assume it's RelWithDebInfo if("${CMAKE_BUILD_TYPE}" STREQUAL "") set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "build type default to RelWithDebInfo, set to Release to improve performance" FORCE) -endif("${CMAKE_BUILD_TYPE}" STREQUAL "") +endif() project(PCL) string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWER) @@ -33,7 +45,7 @@ string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWER) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/" ${CMAKE_MODULE_PATH}) # ---[ Include pkgconfig -include (FindPkgConfig) +include(FindPkgConfig) # ---[ Release/Debug specific flags if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") @@ -90,6 +102,14 @@ elseif(MSVC) set(CMAKE_COMPILER_IS_MSVC 1) endif() +# Create a variable with expected default CXX flags +# This will be used further down the road to check if the user explicitly provided CXX flags +if(CMAKE_COMPILER_IS_MSVC) + set(CMAKE_CXX_FLAGS_DEFAULT "/DWIN32 /D_WINDOWS /W3 /GR /EHsc") +else() + set(CMAKE_CXX_FLAGS_DEFAULT "") +endif() + include("${PCL_SOURCE_DIR}/cmake/pcl_verbosity.cmake") include("${PCL_SOURCE_DIR}/cmake/pcl_targets.cmake") include("${PCL_SOURCE_DIR}/cmake/pcl_options.cmake") @@ -98,29 +118,29 @@ include("${PCL_SOURCE_DIR}/cmake/pcl_options.cmake") if(CMAKE_TIMING_VERBOSE AND UNIX) set_property(GLOBAL PROPERTY RULE_MESSAGES OFF) set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CMAKE_SOURCE_DIR}/cmake/custom_output.sh") -endif(CMAKE_TIMING_VERBOSE AND UNIX) +endif() # check for SSE flags include("${PCL_SOURCE_DIR}/cmake/pcl_find_sse.cmake") -if (PCL_ENABLE_SSE) +if(PCL_ENABLE_SSE AND "${CMAKE_CXX_FLAGS}" STREQUAL "${CMAKE_CXX_FLAGS_DEFAULT}") PCL_CHECK_FOR_SSE() -endif (PCL_ENABLE_SSE) +endif() # ---[ Unix/Darwin/Windows specific flags if(CMAKE_COMPILER_IS_GNUCXX) - if("${CMAKE_CXX_FLAGS}" STREQUAL "") - SET(CMAKE_CXX_FLAGS "-Wall -Wextra -Wno-unknown-pragmas -fno-strict-aliasing -Wno-format-extra-args -Wno-sign-compare -Wno-invalid-offsetof -Wno-conversion ${SSE_FLAGS}") + if("${CMAKE_CXX_FLAGS}" STREQUAL "${CMAKE_CXX_FLAGS_DEFAULT}") + SET(CMAKE_CXX_FLAGS "-Wall -Wextra -Wno-unknown-pragmas -fno-strict-aliasing -Wno-format-extra-args -Wno-sign-compare -Wno-invalid-offsetof -Wno-conversion ${SSE_FLAGS_STR}") # Enable -Wabi for GCC > 4.3, and -Wno-deprecated for GCC < 4.3 # to disable a lot of warnings which are not fixable execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) - if (GCC_VERSION VERSION_GREATER 4.3) + if(GCC_VERSION VERSION_GREATER 4.3) message(STATUS "-- GCC > 4.3 found, enabling -Wabi") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wabi") else() message(STATUS "-- GCC < 4.3 found, enabling -Wno-deprecated") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated") - endif () + endif() endif() if(NOT ANDROID) @@ -134,42 +154,48 @@ if(CMAKE_COMPILER_IS_GNUCXX) if(WIN32) if(PCL_SHARED_LIBS) SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--export-all-symbols -Wl,--enable-auto-import") - if (MINGW) + if(MINGW) add_definitions("-DBOOST_THREAD_USE_LIB") SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--allow-multiple-definition") endif() - else(PCL_SHARED_LIBS) + else() add_definitions("-DBOOST_LIB_DIAGNOSTIC -DBOOST_THREAD_USE_LIB") - endif(PCL_SHARED_LIBS) + endif() endif() endif() if(CMAKE_COMPILER_IS_MSVC) add_definitions("-DBOOST_ALL_NO_LIB -D_SCL_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS -DNOMINMAX -DPCL_ONLY_CORE_POINT_TYPES /bigobj ${SSE_DEFINITIONS}") - if("${CMAKE_CXX_FLAGS}" STREQUAL " /DWIN32 /D_WINDOWS /W3 /GR /EHsc") # Check against default flags - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj /EHsc /fp:precise /wd4800 /wd4521 /wd4251 /wd4275 /wd4305 /wd4355 ${SSE_FLAGS}") + + if("${CMAKE_CXX_FLAGS}" STREQUAL "${CMAKE_CXX_FLAGS_DEFAULT}") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj /fp:precise /wd4800 /wd4521 /wd4251 /wd4275 /wd4305 /wd4355 ${SSE_FLAGS_STR}") # Add extra code generation/link optimizations if(CMAKE_MSVC_CODE_LINK_OPTIMIZATION) SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GL") SET(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /LTCG /OPT:REF") SET(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG") - endif(CMAKE_MSVC_CODE_LINK_OPTIMIZATION) + endif() # /MANIFEST:NO") # please, don't disable manifest generation, otherwise crash at start for vs2008 - if( MSVC_VERSION GREATER 1500 AND ${CMAKE_VERSION} VERSION_GREATER "2.8.6") + if(MSVC_VERSION GREATER 1500 AND ${CMAKE_VERSION} VERSION_GREATER "2.8.6") include(ProcessorCount) ProcessorCount(N) if(NOT N EQUAL 0) - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP${N} ") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP${N} ") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP${N}") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP${N}") endif() endif() endif() + + if(CMAKE_GENERATOR STREQUAL "Ninja") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /FS") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /FS") + endif() endif() if(CMAKE_COMPILER_IS_PATHSCALE) - if("${CMAKE_CXX_FLAGS}" STREQUAL "") + if("${CMAKE_CXX_FLAGS}" STREQUAL "${CMAKE_CXX_FLAGS_DEFAULT}") SET(CMAKE_CXX_FLAGS "-Wno-uninitialized -zerouv -pthread -mp") endif() if("${CMAKE_SHARED_LINKER_FLAGS}" STREQUAL "") @@ -178,11 +204,11 @@ if(CMAKE_COMPILER_IS_PATHSCALE) endif() if(CMAKE_COMPILER_IS_CLANG) - if("${CMAKE_C_FLAGS}" STREQUAL "") + if("${CMAKE_C_FLAGS}" STREQUAL "${CMAKE_CXX_FLAGS_DEFAULT}") SET(CMAKE_C_FLAGS "-Qunused-arguments") endif() if("${CMAKE_CXX_FLAGS}" STREQUAL "") - SET(CMAKE_CXX_FLAGS "-ftemplate-depth=1024 -Qunused-arguments -Wno-invalid-offsetof ${SSE_FLAGS}") # Unfortunately older Clang versions do not have this: -Wno-unnamed-type-template-args + SET(CMAKE_CXX_FLAGS "-ftemplate-depth=1024 -Qunused-arguments -Wno-invalid-offsetof ${SSE_FLAGS_STR}") # Unfortunately older Clang versions do not have this: -Wno-unnamed-type-template-args if(APPLE AND WITH_CUDA AND CUDA_FOUND) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libstdc++") endif() @@ -191,7 +217,7 @@ if(CMAKE_COMPILER_IS_CLANG) endif() include("${PCL_SOURCE_DIR}/cmake/pcl_utils.cmake") -set(PCL_VERSION "1.8.1" CACHE STRING "PCL version") +set(PCL_VERSION "1.9.0" CACHE STRING "PCL version") DISSECT_VERSION() GET_OS_INFO() SET_INSTALL_DIRS() @@ -199,13 +225,16 @@ SET_INSTALL_DIRS() if(WIN32) set(PCL_RESOURCES_DIR "${PCL_SOURCE_DIR}/resources") set(PCL_POINTCLOUDS_DIR "${PCL_RESOURCES_DIR}/pointclouds") -endif(WIN32) +endif() set(PCL_OUTPUT_LIB_DIR "${PCL_BINARY_DIR}/${LIB_INSTALL_DIR}") set(PCL_OUTPUT_BIN_DIR "${PCL_BINARY_DIR}/${BIN_INSTALL_DIR}") make_directory("${PCL_OUTPUT_LIB_DIR}") make_directory("${PCL_OUTPUT_BIN_DIR}") +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PCL_OUTPUT_LIB_DIR}") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PCL_OUTPUT_BIN_DIR}") if(WIN32) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PCL_OUTPUT_BIN_DIR}") foreach(config ${CMAKE_CONFIGURATION_TYPES}) string(TOUPPER ${config} CONFIG) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${CONFIG} "${PCL_OUTPUT_LIB_DIR}") @@ -213,11 +242,9 @@ if(WIN32) # ---[ Windows requires DLLs (shared libraries) to be installed in the same directory as executables set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${CONFIG} "${PCL_OUTPUT_BIN_DIR}") endforeach(config) -else(WIN32) - set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PCL_OUTPUT_LIB_DIR}") - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PCL_OUTPUT_BIN_DIR}") +else() set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PCL_OUTPUT_LIB_DIR}") -endif(WIN32) +endif() # Add an "uninstall" target configure_file("${PCL_SOURCE_DIR}/cmake/uninstall_target.cmake.in" @@ -246,7 +273,7 @@ endif() if(OPENMP_FOUND) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - message (STATUS "Found OpenMP") + message(STATUS "Found OpenMP") if(MSVC) if(MSVC_VERSION EQUAL 1500) set(OPENMP_DLL VCOMP90) @@ -258,18 +285,18 @@ if(OPENMP_FOUND) set(OPENMP_DLL VCOMP120) elseif(MSVC_VERSION EQUAL 1900) set(OPENMP_DLL VCOMP140) - elseif(MSVC_VERSION EQUAL 1910) + elseif(MSVC_VERSION MATCHES "^191[0-9]$") set(OPENMP_DLL VCOMP140) endif() if(OPENMP_DLL) set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} /DELAYLOAD:${OPENMP_DLL}D.dll") set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DELAYLOAD:${OPENMP_DLL}.dll") - else(OPENMP_DLL) + else() message(WARNING "Delay loading flag for OpenMP DLL is invalid.") - endif(OPENMP_DLL) + endif() endif(MSVC) -else(OPENMP_FOUND) - message (STATUS "Not found OpenMP") +else() + message(STATUS "Not found OpenMP") endif() # Eigen (required) @@ -279,7 +306,7 @@ include_directories(SYSTEM ${EIGEN_INCLUDE_DIRS}) # FLANN (required) if(NOT PCL_SHARED_LIBS OR ((WIN32 AND NOT MINGW) AND NOT PCL_BUILD_WITH_FLANN_DYNAMIC_LINKING_WIN32)) set(FLANN_USE_STATIC ON) -endif(NOT PCL_SHARED_LIBS OR ((WIN32 AND NOT MINGW) AND NOT PCL_BUILD_WITH_FLANN_DYNAMIC_LINKING_WIN32)) +endif() find_package(FLANN 1.7.0 REQUIRED) include_directories(${FLANN_INCLUDE_DIRS}) @@ -289,8 +316,8 @@ if(WITH_LIBUSB) find_package(libusb-1.0) if(LIBUSB_1_FOUND) include_directories("${LIBUSB_1_INCLUDE_DIR}") - endif(LIBUSB_1_FOUND) -endif(WITH_LIBUSB) + endif() +endif() # Dependencies for different grabbers PCL_ADD_GRABBER_DEPENDENCY("OpenNI" "OpenNI grabber support") @@ -302,10 +329,10 @@ PCL_ADD_GRABBER_DEPENDENCY("DSSDK" "DepthSense SDK support") PCL_ADD_GRABBER_DEPENDENCY("RSSDK" "RealSense SDK support") # metslib -if (PKG_CONFIG_FOUND) +if(PKG_CONFIG_FOUND) pkg_check_modules(METSLIB metslib) - if (METSLIB_FOUND) - set (HAVE_METSLIB ON) + if(METSLIB_FOUND) + set(HAVE_METSLIB ON) include_directories(${METSLIB_INCLUDE_DIRS}) else() include_directories("${PCL_SOURCE_DIR}/recognition/include/pcl/recognition/3rdparty/") @@ -318,44 +345,44 @@ endif() option(WITH_PNG "PNG file support" TRUE) if(WITH_PNG) find_package(PNG) - if (PNG_FOUND) - set (HAVE_PNG ON) + if(PNG_FOUND) + set(HAVE_PNG ON) include_directories("${PNG_INCLUDE_DIR}") - endif(PNG_FOUND) -endif(WITH_PNG) + endif() +endif() # Qhull option(WITH_QHULL "Include convex-hull operations" TRUE) if(WITH_QHULL) - if(NOT PCL_SHARED_LIBS OR WIN32) + if(NOT PCL_SHARED_LIBS OR ((WIN32 AND NOT MINGW) AND NOT PCL_BUILD_WITH_QHULL_DYNAMIC_LINKING_WIN32)) set(QHULL_USE_STATIC ON) - endif(NOT PCL_SHARED_LIBS OR WIN32) + endif() find_package(Qhull) if(QHULL_FOUND) include_directories(${QHULL_INCLUDE_DIRS}) - endif(QHULL_FOUND) -endif(WITH_QHULL) + endif() +endif() # Cuda option(WITH_CUDA "Build NVIDIA-CUDA support" TRUE) if(WITH_CUDA) include("${PCL_SOURCE_DIR}/cmake/pcl_find_cuda.cmake") -endif(WITH_CUDA) +endif() option(WITH_QT "Build QT Front-End" TRUE) if(WITH_QT) set(PCL_QT_VERSION 5 CACHE STRING "Which QT version to use") if("${PCL_QT_VERSION}" STREQUAL "4") find_package(Qt4) - if (QT4_FOUND) + if(QT4_FOUND) include("${QT_USE_FILE}") - endif (QT4_FOUND) + endif() elseif("${PCL_QT_VERSION}" STREQUAL "5") include(cmake/pcl_find_qt5.cmake) else() message(SEND_ERROR "PCL_QT_VERSION must be 4 or 5") endif() -endif(WITH_QT) +endif() # Find VTK option(WITH_VTK "Build VTK-Visualizations" TRUE) @@ -368,51 +395,50 @@ if(WITH_VTK AND NOT ANDROID) set(VTK_RENDERING_BACKEND "OpenGL") endif() message(STATUS "VTK_MAJOR_VERSION ${VTK_MAJOR_VERSION}, rendering backend: ${VTK_RENDERING_BACKEND}") - if (PCL_SHARED_LIBS OR - (NOT (PCL_SHARED_LIBS) AND NOT (VTK_BUILD_SHARED_LIBS))) + if(PCL_SHARED_LIBS OR (NOT (PCL_SHARED_LIBS) AND NOT (VTK_BUILD_SHARED_LIBS))) set(VTK_FOUND TRUE) - find_package (QVTK) - if (${VTK_MAJOR_VERSION} VERSION_LESS "6.0") + find_package(QVTK) + if(${VTK_MAJOR_VERSION} VERSION_LESS "6.0") message(STATUS "VTK found (include: ${VTK_INCLUDE_DIRS}, lib: ${VTK_LIBRARY_DIRS})") link_directories(${VTK_LIBRARY_DIRS}) - else(${VTK_MAJOR_VERSION} VERSION_LESS "6.0") - include (${VTK_USE_FILE}) + else() + include(${VTK_USE_FILE}) message(STATUS "VTK found (include: ${VTK_INCLUDE_DIRS}, lib: ${VTK_LIBRARIES}") - endif (${VTK_MAJOR_VERSION} VERSION_LESS "6.0") - if (APPLE) - option (VTK_USE_COCOA "Use Cocoa for VTK render windows" ON) - MARK_AS_ADVANCED (VTK_USE_COCOA) - endif (APPLE) + endif() + if(APPLE) + option(VTK_USE_COCOA "Use Cocoa for VTK render windows" ON) + MARK_AS_ADVANCED(VTK_USE_COCOA) + endif() if(${VTK_RENDERING_BACKEND} STREQUAL "OpenGL") set(VTK_RENDERING_BACKEND_OPENGL_VERSION "1") elseif(${VTK_RENDERING_BACKEND} STREQUAL "OpenGL2") set(VTK_RENDERING_BACKEND_OPENGL_VERSION "2") endif() set(HAVE_VTK ON) - else () + else() set(VTK_FOUND OFF) set(HAVE_VTK OFF) - message ("Warning: You are to build PCL in STATIC but VTK is SHARED!") - message ("Warning: VTK disabled!") - endif () - endif(VTK_FOUND) -else(WITH_VTK AND NOT ANDROID) + message("Warning: You are to build PCL in STATIC but VTK is SHARED!") + message("Warning: VTK disabled!") + endif() + endif() +else() set(VTK_FOUND OFF) set(HAVE_VTK OFF) -endif(WITH_VTK AND NOT ANDROID) +endif() #Find PCAP option(WITH_PCAP "pcap file capabilities in Velodyne HDL driver" TRUE) if(WITH_PCAP) find_package(Pcap) -endif(WITH_PCAP) +endif() # OpenGL and GLUT option(WITH_OPENGL "Support for OpenGL" TRUE) if(WITH_OPENGL) include("${PCL_SOURCE_DIR}/cmake/pcl_find_gl.cmake") -endif(WITH_OPENGL) +endif() # Boost (required) include("${PCL_SOURCE_DIR}/cmake/pcl_find_boost.cmake") @@ -456,7 +482,7 @@ if(CPACK_GENERATOR) PCL_MAKE_CPACK_INPUT() set(CPACK_PROJECT_CONFIG_FILE "${PCL_CPACK_CFG_FILE}") include(CPack) -endif(CPACK_GENERATOR) +endif() ### ---[ Make a pretty picture of the dependency graph include("${PCL_SOURCE_DIR}/cmake/dep_graph.cmake") MAKE_DEP_GRAPH() diff --git a/PCLConfig.cmake.in b/PCLConfig.cmake.in index f4ef6a0f..7ec40baa 100644 --- a/PCLConfig.cmake.in +++ b/PCLConfig.cmake.in @@ -1,21 +1,33 @@ # ------------------------------------------------------------------------------------ # Helper to use PCL from outside project # -# target_link_libraries(my_fabulous_target PCL_XXX_LIBRARIES) where XXX is the -# upper cased xxx from : +# target_link_libraries(my_fabulous_target PCL_XXX_LIBRARIES) where XXX is the +# upper cased xxx from : # @PCLCONFIG_AVAILABLE_COMPONENTS_LIST@ # # PCL_INCLUDE_DIRS is filled with PCL and available 3rdparty headers # PCL_LIBRARY_DIRS is filled with PCL components libraries install directory and # 3rdparty libraries paths -# +# # www.pointclouds.org #------------------------------------------------------------------------------------ +if(POLICY CMP0074) + # TODO: update *_ROOT variables to be PCL_*_ROOT or equivalent. + # CMP0074 directly affects how Find* modules work and *_ROOT variables. Since + # this is a config file that will be consumed by parent projects with (likely) + # NEW behavior, we need to push a policy stack. + cmake_policy(PUSH) + cmake_policy(SET CMP0074 OLD) +endif() + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/Modules") + ### ---[ some useful macros macro(pcl_report_not_found _reason) unset(PCL_FOUND) unset(PCL_LIBRARIES) + unset(PCL_COMPONENTS) unset(PCL_INCLUDE_DIRS) unset(PCL_LIBRARY_DIRS) unset(PCL_DEFINITIONS) @@ -33,7 +45,7 @@ macro(pcl_message) endif(NOT PCL_FIND_QUIETLY) endmacro(pcl_message) -# Remove duplicate libraries +# Remove duplicate libraries macro(pcl_remove_duplicate_libraries _unfiltered_libraries _filtered_libraries) set(${_filtered_libraries}) set(_debug_libraries) @@ -91,6 +103,7 @@ macro(find_boost) else(${CMAKE_VERSION} VERSION_LESS 2.8.5) set(Boost_ADDITIONAL_VERSIONS "@Boost_MAJOR_VERSION@.@Boost_MINOR_VERSION@.@Boost_SUBMINOR_VERSION@" "@Boost_MAJOR_VERSION@.@Boost_MINOR_VERSION@" + "1.68.0" "1.68" "1.67.0" "1.67" "1.66.0" "1.66" "1.65.1" "1.65.0" "1.65" "1.64.0" "1.64" "1.63.0" "1.63" "1.62.0" "1.62" "1.61.0" "1.61" "1.60.0" "1.60" "1.59.0" "1.59" "1.58.0" "1.58" "1.57.0" "1.57" "1.56.0" "1.56" "1.55.0" "1.55" "1.54.0" "1.54" "1.53.0" "1.53" "1.52.0" "1.52" "1.51.0" "1.51" @@ -116,16 +129,7 @@ macro(find_eigen) elseif(NOT EIGEN_ROOT) get_filename_component(EIGEN_ROOT "@EIGEN_INCLUDE_DIRS@" ABSOLUTE) endif(PCL_ALL_IN_ONE_INSTALLER) - if(PKG_CONFIG_FOUND) - pkg_check_modules(PC_EIGEN eigen3) - endif(PKG_CONFIG_FOUND) - find_path(EIGEN_INCLUDE_DIRS Eigen/Core - HINTS ${PC_EIGEN_INCLUDEDIR} ${PC_EIGEN_INCLUDE_DIRS} - "${EIGEN_ROOT}" "$ENV{EIGEN_ROOT}" - PATHS "$ENV{PROGRAMFILES}/Eigen3" "$ENV{PROGRAMW6432}/Eigen3" - "$ENV{PROGRAMFILES}/Eigen" "$ENV{PROGRAMW6432}/Eigen" - PATH_SUFFIXES eigen3 include/eigen3 include) - find_package_handle_standard_args(eigen DEFAULT_MSG EIGEN_INCLUDE_DIRS) + find_package(Eigen) set(EIGEN_DEFINITIONS ${EIGEN_DEFINITIONS}) endmacro(find_eigen) @@ -136,84 +140,23 @@ macro(find_qhull) elseif(NOT QHULL_ROOT) get_filename_component(QHULL_ROOT "@QHULL_INCLUDE_DIRS@" PATH) endif(PCL_ALL_IN_ONE_INSTALLER) - set(QHULL_MAJOR_VERSION 6) - - find_path(QHULL_INCLUDE_DIRS - NAMES libqhull/libqhull.h qhull.h - HINTS "${QHULL_ROOT}" "$ENV{QHULL_ROOT}" - PATHS "$ENV{PROGRAMFILES}/qhull" "$ENV{PROGRAMW6432}/qhull" - PATH_SUFFIXES qhull src/libqhull libqhull include) - - # Most likely we are on windows so prefer static libraries over shared ones (Mourad's recommend) - find_library(QHULL_LIBRARY - NAMES "@QHULL_LIBRARY_NAME@" - HINTS "${QHULL_ROOT}" "$ENV{QHULL_ROOT}" - PATHS "$ENV{PROGRAMFILES}/qhull" "$ENV{PROGRAMW6432}/qhull" - PATH_SUFFIXES project build bin lib) - - find_library(QHULL_LIBRARY_DEBUG - NAMES "@QHULL_LIBRARY_DEBUG_NAME@" - HINTS "${QHULL_ROOT}" "$ENV{QHULL_ROOT}" - PATHS "$ENV{PROGRAMFILES}/qhull" "$ENV{PROGRAMW6432}/qhull" - PATH_SUFFIXES project build bin lib) - - find_package_handle_standard_args(qhull DEFAULT_MSG QHULL_LIBRARY QHULL_INCLUDE_DIRS) - - if(QHULL_FOUND) - get_filename_component(QHULL_LIBRARY_PATH ${QHULL_LIBRARY} PATH) - if(QHULL_LIBRARY_DEBUG) - set(QHULL_LIBRARIES optimized ${QHULL_LIBRARY} debug ${QHULL_LIBRARY_DEBUG}) - get_filename_component(QHULL_LIBRARY_DEBUG_PATH ${QHULL_LIBRARY_DEBUG} PATH) - set(QHULL_LIBRARY_DIRS ${QHULL_LIBRARY_PATH} ${QHULL_LIBRARY_DEBUG_PATH}) - else(QHULL_LIBRARY_DEBUG) - set(QHULL_LIBRARIES ${QHULL_LIBRARY}) - set(QHULL_LIBRARY_DIRS ${QHULL_LIBRARY_PATH}) - endif(QHULL_LIBRARY_DEBUG) - set(QHULL_DEFINITIONS) - get_filename_component(qhull_lib ${QHULL_LIBRARY} NAME_WE) - if(NOT "${qhull_lib}" MATCHES "qhullstatic") - set(QHULL_DEFINITIONS ${QHULL_DEFINITIONS} -Dqh_QHpointer) - if(MSVC) - set(QHULL_DEFINITIONS ${QHULL_DEFINITIONS} -Dqh_QHpointer_dllimport) - endif(MSVC) - endif(NOT "${qhull_lib}" MATCHES "qhullstatic") - endif(QHULL_FOUND) + + set(QHULL_USE_STATIC @QHULL_USE_STATIC@) + find_package(Qhull) endmacro(find_qhull) #remove this as soon as libopenni is shipped with FindOpenni.cmake macro(find_openni) + if(PCL_FIND_QUIETLY) + set(OpenNI_FIND_QUIETLY TRUE) + endif() + if(NOT OPENNI_ROOT AND ("@HAVE_OPENNI@" STREQUAL "TRUE")) set(OPENNI_INCLUDE_DIRS_HINT "@OPENNI_INCLUDE_DIRS@") get_filename_component(OPENNI_LIBRARY_HINT "@OPENNI_LIBRARY@" PATH) endif() - if(PKG_CONFIG_FOUND) - pkg_check_modules(PC_OPENNI libopenni) - endif(PKG_CONFIG_FOUND) - find_path(OPENNI_INCLUDE_DIRS XnStatus.h - HINTS ${PC_OPENNI_INCLUDEDIR} ${PC_OPENNI_INCLUDE_DIRS} - "${OPENNI_ROOT}" "$ENV{OPENNI_ROOT}" - PATHS "$ENV{OPEN_NI_INCLUDE}" "${OPENNI_INCLUDE_DIRS_HINT}" - PATH_SUFFIXES include/openni Include) - #add a hint so that it can find it without the pkg-config - set(OPENNI_SUFFIX) - if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8) - set(OPENNI_SUFFIX 64) - endif(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8) - find_library(OPENNI_LIBRARY - NAMES OpenNI64 OpenNI - HINTS ${PC_OPENNI_LIBDIR} ${PC_OPENNI_LIBRARY_DIRS} - "${OPENNI_ROOT}" "$ENV{OPENNI_ROOT}" - PATHS "$ENV{OPEN_NI_LIB${OPENNI_SUFFIX}}" "${OPENNI_LIBRARY_HINT}" - PATH_SUFFIXES lib Lib Lib64) - - find_package_handle_standard_args(openni DEFAULT_MSG OPENNI_LIBRARY OPENNI_INCLUDE_DIRS) - - if(OPENNI_FOUND) - get_filename_component(OPENNI_LIBRARY_PATH ${OPENNI_LIBRARY} PATH) - set(OPENNI_LIBRARY_DIRS ${OPENNI_LIBRARY_PATH}) - set(OPENNI_LIBRARIES "${OPENNI_LIBRARY}") - endif(OPENNI_FOUND) + find_package(OpenNI) endmacro(find_openni) #remove this as soon as libopenni2 is shipped with FindOpenni2.cmake @@ -227,39 +170,7 @@ macro(find_openni2) get_filename_component(OPENNI2_LIBRARY_HINT "@OPENNI2_LIBRARY@" PATH) endif() - set(OPENNI2_SUFFIX) - if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8) - set(OPENNI2_SUFFIX 64) - endif(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8) - - if(PKG_CONFIG_FOUND) - if(PCL_FIND_QUIETLY) - pkg_check_modules(PC_OPENNI2 QUIET libopenni2) - else() - pkg_check_modules(PC_OPENNI2 libopenni2) - endif() - endif(PKG_CONFIG_FOUND) - - find_path(OPENNI2_INCLUDE_DIRS OpenNI.h - HINTS /usr/include/openni2 /usr/include/ni2 /usr/local/include/openni2 /usr/local/include/ni2 - PATHS "$ENV{OPENNI2_INCLUDE${OPENNI2_SUFFIX}}" "${OPENNI2_INCLUDE_DIRS_HINT}" - PATH_SUFFIXES openni openni2 include Include) - - find_library(OPENNI2_LIBRARY - NAMES OpenNI2 # No suffix needed on Win64 - HINTS /usr/lib /usr/local/lib/ni2 - PATHS "$ENV{OPENNI2_LIB${OPENNI2_SUFFIX}}" "${OPENNI2_LIBRARY_HINT}" - PATH_SUFFIXES lib Lib Lib64) - - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(OpenNI2 DEFAULT_MSG OPENNI2_LIBRARY OPENNI2_INCLUDE_DIRS) - - if(OPENNI2_FOUND) - get_filename_component(OPENNI2_LIBRARY_PATH ${OPENNI2_LIBRARY} PATH) - set(OPENNI2_LIBRARY_DIRS ${OPENNI2_LIBRARY_PATH}) - set(OPENNI2_LIBRARIES "${OPENNI2_LIBRARY}") - set(OPENNI2_REDIST_DIR $ENV{OPENNI2_REDIST${OPENNI2_SUFFIX}}) - endif(OPENNI2_FOUND) + find_package(OpenNI2) endmacro(find_openni2) #remove this as soon as the Ensenso SDK is shipped with FindEnsenso.cmake @@ -272,28 +183,7 @@ macro(find_ensenso) get_filename_component(ENSENSO_ABI_HINT "@ENSENSO_INCLUDE_DIR@" PATH) endif() - find_path(ENSENSO_INCLUDE_DIR nxLib.h - HINTS ${ENSENSO_ABI_HINT} - /opt/ensenso/development/c - "$ENV{PROGRAMFILES}/Ensenso/development/c" "$ENV{PROGRAMW6432}/Ensenso/development/c" - PATH_SUFFIXES include/) - - find_library(ENSENSO_LIBRARY QUIET NAMES NxLib64 NxLib32 nxLib64 nxLib32 - HINTS ${ENSENSO_ABI_HINT} - "$ENV{PROGRAMFILES}/Ensenso/development/c" "$ENV{PROGRAMW6432}/Ensenso/development/c" - PATH_SUFFIXES lib/) - - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(ensenso DEFAULT_MSG - ENSENSO_LIBRARY ENSENSO_INCLUDE_DIR) - - if(ENSENSO_FOUND) - get_filename_component(ENSENSO_LIBRARY_PATH ${ENSENSO_LIBRARY} PATH) - set(ENSENSO_INCLUDE_DIRS ${ENSENSO_INCLUDE_DIR}) - set(ENSENSO_LIBRARY_DIRS ${ENSENSO_LIBRARY_PATH}) - set(ENSENSO_LIBRARIES "${ENSENSO_LIBRARY}") - set(ENSENSO_REDIST_DIR $ENV{ENSENSO_REDIST${ENSENSO_SUFFIX}}) - endif(ENSENSO_FOUND) + find_package(Ensenso) endmacro(find_ensenso) #remove this as soon as the davidSDK is shipped with FinddavidSDK.cmake @@ -306,28 +196,7 @@ macro(find_davidSDK) get_filename_component(DAVIDSDK_ABI_HINT @DAVIDSDK_INCLUDE_DIR@ PATH) endif() - find_path(DAVIDSDK_INCLUDE_DIR david.h - HINTS ${DAVIDSDK_ABI_HINT} - /usr/local/include/davidSDK - "$ENV{PROGRAMFILES}/davidSDK" "$ENV{PROGRAMW6432}/davidSDK" - PATH_SUFFIXES include/) - - find_library(DAVIDSDK_LIBRARY QUIET NAMES davidSDK - HINTS ${DAVIDSDK_ABI_HINT} - "$ENV{PROGRAMFILES}/davidSDK" "$ENV{PROGRAMW6432}/davidSDK" - PATH_SUFFIXES lib/) - - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(DAVIDSDK DEFAULT_MSG - DAVIDSDK_LIBRARY DAVIDSDK_INCLUDE_DIR) - - if(DAVIDSDK_FOUND) - get_filename_component(DAVIDSDK_LIBRARY_PATH ${DAVIDSDK_LIBRARY} PATH) - set(DAVIDSDK_INCLUDE_DIRS ${DAVIDSDK_INCLUDE_DIR}) - set(DAVIDSDK_LIBRARY_DIRS ${DAVIDSDK_LIBRARY_PATH}) - set(DAVIDSDK_LIBRARIES "${DAVIDSDK_LIBRARY}") - set(DAVIDSDK_REDIST_DIR $ENV{DAVIDSDK_REDIST${DAVIDSDK_SUFFIX}}) - endif(DAVIDSDK_FOUND) + find_package(davidSDK) endmacro(find_davidSDK) macro(find_dssdk) @@ -337,36 +206,8 @@ macro(find_dssdk) if(NOT DSSDK_DIR AND ("@HAVE_DSSDK@" STREQUAL "TRUE")) get_filename_component(DSSDK_DIR_HINT "@DSSDK_INCLUDE_DIRS@" PATH) endif() - find_path(DSSDK_DIR include/DepthSenseVersion.hxx - HINTS ${DSSDK_DIR_HINT} - "$ENV{DEPTHSENSESDK32}" - "$ENV{DEPTHSENSESDK64}" - PATHS "$ENV{PROGRAMFILES}/SoftKinetic/DepthSenseSDK" - "$ENV{PROGRAMW6432}/SoftKinetic/DepthSenseSDK" - "C:/Program Files (x86)/SoftKinetic/DepthSenseSDK" - "C:/Program Files/SoftKinetic/DepthSenseSDK" - "/opt/softkinetic/DepthSenseSDK" - DOC "DepthSense SDK directory") - set(_DSSDK_INCLUDE_DIRS ${DSSDK_DIR}/include) - if(MSVC) - set(DSSDK_LIBRARIES_NAMES DepthSense) - else() - set(DSSDK_LIBRARIES_NAMES DepthSense DepthSensePlugins turbojpeg) - endif() - foreach(LIB ${DSSDK_LIBRARIES_NAMES}) - find_library(DSSDK_LIBRARY_${LIB} - NAMES "${LIB}" - PATHS "${DSSDK_DIR}/lib/" NO_DEFAULT_PATH) - list(APPEND _DSSDK_LIBRARIES ${DSSDK_LIBRARY_${LIB}}) - mark_as_advanced(DSSDK_LIBRARY_${LIB}) - endforeach() - find_package_handle_standard_args(DSSDK DEFAULT_MSG _DSSDK_LIBRARIES _DSSDK_INCLUDE_DIRS) - if(DSSDK_FOUND) - set(DSSDK_LIBRARIES ${_DSSDK_LIBRARIES}) - mark_as_advanced(DSSDK_LIBRARIES) - set(DSSDK_INCLUDE_DIRS ${_DSSDK_INCLUDE_DIRS}) - mark_as_advanced(DSSDK_INCLUDE_DIRS) - endif() + + find_package(DSSDK) endmacro(find_dssdk) macro(find_rssdk) @@ -376,41 +217,8 @@ macro(find_rssdk) if(NOT RSSDK_DIR AND ("@HAVE_RSSDK@" STREQUAL "TRUE")) get_filename_component(RSSDK_DIR_HINT "@RSSDK_INCLUDE_DIRS@" PATH) endif() - find_path(RSSDK_DIR include/pxcversion.h - HINTS ${RSSDK_DIR_HINT} - PATHS "$ENV{RSSDK_DIR}" - "$ENV{PROGRAMFILES}/Intel/RSSDK" - "$ENV{PROGRAMW6432}/Intel/RSSDK" - "C:/Program Files (x86)/Intel/RSSDK" - "C:/Program Files/Intel/RSSDK" - DOC "RealSense SDK directory") - set(_RSSDK_INCLUDE_DIRS ${RSSDK_DIR}/include) - set(RSSDK_RELEASE_NAME libpxc.lib) - set(RSSDK_DEBUG_NAME libpxc_d.lib) - set(RSSDK_SUFFIX Win32) - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(RSSDK_SUFFIX x64) - endif() - find_library(RSSDK_LIBRARY - NAMES ${RSSDK_RELEASE_NAME} - PATHS "${RSSDK_DIR}/lib/" NO_DEFAULT_PATH - PATH_SUFFIXES ${RSSDK_SUFFIX}) - find_library(RSSDK_LIBRARY_DEBUG - NAMES ${RSSDK_DEBUG_NAME} ${RSSDK_RELEASE_NAME} - PATHS "${RSSDK_DIR}/lib/" NO_DEFAULT_PATH - PATH_SUFFIXES ${RSSDK_SUFFIX}) - if(NOT RSSDK_LIBRARY_DEBUG) - set(RSSDK_LIBRARY_DEBUG ${RSSDK_LIBRARY}) - endif() - set(_RSSDK_LIBRARIES optimized ${RSSDK_LIBRARY} debug ${RSSDK_LIBRARY_DEBUG}) - mark_as_advanced(RSSDK_LIBRARY RSSDK_LIBRARY_DEBUG) - find_package_handle_standard_args(RSSDK DEFAULT_MSG _RSSDK_LIBRARIES _RSSDK_INCLUDE_DIRS) - if(RSSDK_FOUND) - set(RSSDK_LIBRARIES ${_RSSDK_LIBRARIES}) - mark_as_advanced(RSSDK_LIBRARIES) - set(RSSDK_INCLUDE_DIRS ${_RSSDK_INCLUDE_DIRS}) - mark_as_advanced(RSSDK_INCLUDE_DIRS) - endif() + + find_package(RSSDK) endmacro(find_rssdk) #remove this as soon as flann is shipped with FindFlann.cmake @@ -420,46 +228,9 @@ macro(find_flann) elseif(NOT FLANN_ROOT) get_filename_component(FLANN_ROOT "@FLANN_INCLUDE_DIRS@" PATH) endif(PCL_ALL_IN_ONE_INSTALLER) - if(PKG_CONFIG_FOUND) - pkg_check_modules(PC_FLANN flann) - endif(PKG_CONFIG_FOUND) - - find_path(FLANN_INCLUDE_DIRS flann/flann.hpp - HINTS ${PC_FLANN_INCLUDEDIR} ${PC_FLANN_INCLUDE_DIRS} - "${FLANN_ROOT}" "$ENV{FLANN_ROOT}" - PATHS "$ENV{PROGRAMFILES}/flann 1.6.9" "$ENV{PROGRAMW6432}/flann 1.6.9" - "$ENV{PROGRAMFILES}/flann" "$ENV{PROGRAMW6432}/flann" - PATH_SUFFIXES include) - - find_library(FLANN_LIBRARY - NAMES flann_cpp_s flann_cpp - HINTS ${PC_FLANN_LIBDIR} ${PC_FLANN_LIBRARY_DIRS} "${FLANN_ROOT}" "$ENV{FLANN_ROOT}" - PATHS "$ENV{PROGRAMFILES}/flann 1.6.9" "$ENV{PROGRAMW6432}/flann 1.6.9" - "$ENV{PROGRAMFILES}/flann" "$ENV{PROGRAMW6432}/flann" - PATH_SUFFIXES lib) - - find_library(FLANN_LIBRARY_DEBUG - NAMES flann_cpp_s-gd flann_cpp-gd flann_cpp_s flann_cpp - HINTS ${PC_FLANN_LIBDIR} ${PC_FLANN_LIBRARY_DIRS} "${FLANN_ROOT}" "$ENV{FLANN_ROOT}" - PATHS "$ENV{PROGRAMFILES}/flann 1.6.9" "$ENV{PROGRAMW6432}/flann 1.6.9" - "$ENV{PROGRAMFILES}/flann" "$ENV{PROGRAMW6432}/flann" - PATH_SUFFIXES lib) - - find_package_handle_standard_args(Flann DEFAULT_MSG FLANN_LIBRARY FLANN_INCLUDE_DIRS) - if(FLANN_FOUND) - get_filename_component(FLANN_LIBRARY_PATH ${FLANN_LIBRARY} PATH) - if(FLANN_LIBRARY_DEBUG) - get_filename_component(FLANN_LIBRARY_DEBUG_PATH ${FLANN_LIBRARY_DEBUG} PATH) - set(FLANN_LIBRARY_DIRS ${FLANN_LIBRARY_PATH} ${FLANN_LIBRARY_DEBUG_PATH}) - set(FLANN_LIBRARIES optimized ${FLANN_LIBRARY} debug ${FLANN_LIBRARY_DEBUG}) - else(FLANN_LIBRARY_DEBUG) - set(FLANN_LIBRARY_DIRS ${FLANN_LIBRARY_PATH}) - set(FLANN_LIBRARIES ${FLANN_LIBRARY}) - endif(FLANN_LIBRARY_DEBUG) - if("${FLANN_LIBRARY}" MATCHES "flann_cpp_s") - set(FLANN_DEFINITIONS ${FLANN_DEFINITIONS} -DFLANN_STATIC) - endif("${FLANN_LIBRARY}" MATCHES "flann_cpp_s") - endif(FLANN_FOUND) + + set(FLANN_USE_STATIC @FLANN_USE_STATIC@) + find_package(FLANN) endmacro(find_flann) macro(find_VTK) @@ -492,105 +263,7 @@ macro(find_libusb) endmacro(find_libusb) macro(find_glew) -IF (WIN32) - - IF(CYGWIN) - - FIND_PATH( GLEW_INCLUDE_DIR GL/glew.h) - - FIND_LIBRARY( GLEW_GLEW_LIBRARY glew32 - ${OPENGL_LIBRARY_DIR} - /usr/lib/w32api - /usr/X11R6/lib - ) - - - ELSE(CYGWIN) - - FIND_PATH( GLEW_INCLUDE_DIR GL/glew.h - $ENV{GLEW_ROOT}/include - ) - - FIND_LIBRARY( GLEW_GLEW_LIBRARY - NAMES glew glew32 glew32s - PATHS - $ENV{GLEW_ROOT}/lib - ${OPENGL_LIBRARY_DIR} - ) - - ENDIF(CYGWIN) - -ELSE (WIN32) - - IF (APPLE) - - FIND_PATH( GLEW_INCLUDE_DIR GL/glew.h - /usr/local/include - /System/Library/Frameworks/GLEW.framework/Versions/A/Headers - ${OPENGL_LIBRARY_DIR} - ) - - FIND_LIBRARY( GLEW_GLEW_LIBRARY GLEW - /usr/local/lib - /usr/openwin/lib - /usr/X11R6/lib - ) - - if(NOT GLEW_GLEW_LIBRARY) - SET(GLEW_GLEW_LIBRARY "-framework GLEW" CACHE STRING "GLEW library for OSX") - SET(GLEW_cocoa_LIBRARY "-framework Cocoa" CACHE STRING "Cocoa framework for OSX") - endif() - ELSE (APPLE) - - FIND_PATH( GLEW_INCLUDE_DIR GL/glew.h - /usr/include/GL - /usr/openwin/share/include - /usr/openwin/include - /usr/X11R6/include - /usr/include/X11 - /opt/graphics/OpenGL/include - /opt/graphics/OpenGL/contrib/libglew - ) - - FIND_LIBRARY( GLEW_GLEW_LIBRARY GLEW - /usr/openwin/lib - /usr/X11R6/lib - ) - - ENDIF (APPLE) - -ENDIF (WIN32) - -SET( GLEW_FOUND FALSE ) -IF(GLEW_INCLUDE_DIR) - IF(GLEW_GLEW_LIBRARY) - # Is -lXi and -lXmu required on all platforms that have it? - # If not, we need some way to figure out what platform we are on. - SET( GLEW_LIBRARIES - ${GLEW_GLEW_LIBRARY} - ${GLEW_cocoa_LIBRARY} - ) - SET( GLEW_FOUND TRUE ) - -#The following deprecated settings are for backwards compatibility with CMake1.4 - SET (GLEW_LIBRARY ${GLEW_LIBRARIES}) - SET (GLEW_INCLUDE_PATH ${GLEW_INCLUDE_DIR}) - - ENDIF(GLEW_GLEW_LIBRARY) -ENDIF(GLEW_INCLUDE_DIR) - -IF(GLEW_FOUND) - IF(NOT GLEW_FIND_QUIETLY) - MESSAGE(STATUS "Found Glew: ${GLEW_LIBRARIES}") - ENDIF(NOT GLEW_FIND_QUIETLY) - IF(GLEW_GLEW_LIBRARY MATCHES glew32s) - list(APPEND PCL_DEFINITIONS "-DGLEW_STATIC") - ENDIF(GLEW_GLEW_LIBRARY MATCHES glew32s) -ELSE(GLEW_FOUND) - IF(GLEW_FIND_REQUIRED) - MESSAGE(FATAL_ERROR "Could not find Glew") - ENDIF(GLEW_FIND_REQUIRED) -ENDIF(GLEW_FOUND) + find_package(GLEW) endmacro(find_glew) # Finds each component external libraries if any @@ -599,10 +272,10 @@ endmacro(find_glew) # |--> _lib found ==> include the headers, # | link to its library directories or include _lib_USE_FILE # `--> _lib not found -# |--> _lib is optional ==> disable it (thanks to the guardians) +# |--> _lib is optional ==> disable it (thanks to the guardians) # | and warn # `--> _lib is required -# |--> component is required explicitely ==> error +# |--> component is required explicitly ==> error # `--> component is induced ==> warn and remove it # from the list @@ -744,7 +417,7 @@ elseif(EXISTS "${PCL_DIR}/include/pcl/pcl_config.h") set(PCL_LIBRARY_DIRS "${PCL_DIR}/@LIB_INSTALL_DIR@") set(PCL_SOURCES_TREE "@CMAKE_SOURCE_DIR@") else(EXISTS "${PCL_ROOT}/include/pcl-${PCL_VERSION_MAJOR}.${PCL_VERSION_MINOR}/pcl/pcl_config.h") - pcl_report_not_found("PCL can not be found on this machine") + pcl_report_not_found("PCL can not be found on this machine") endif(EXISTS "${PCL_ROOT}/include/pcl-${PCL_VERSION_MAJOR}.${PCL_VERSION_MINOR}/pcl/pcl_config.h") #set a suffix for debug libraries @@ -752,7 +425,8 @@ set(PCL_DEBUG_SUFFIX "@CMAKE_DEBUG_POSTFIX@") set(PCL_RELEASE_SUFFIX "@CMAKE_RELEASE_POSTFIX@") #set SSE flags used compiling PCL -list(APPEND PCL_DEFINITIONS "@PCLCONFIG_SSE_DEFINITIONS@") +list(APPEND PCL_DEFINITIONS @PCLCONFIG_SSE_DEFINITIONS@) +list(APPEND PCL_COMPILE_OPTIONS @PCLCONFIG_SSE_COMPILE_OPTIONS@) set(pcl_all_components @PCLCONFIG_AVAILABLE_COMPONENTS@ ) list(LENGTH pcl_all_components PCL_NB_COMPONENTS) @@ -777,7 +451,7 @@ if(PCL_FIND_COMPONENTS) set(PCL_TO_FIND_COMPONENTS ${pcl_all_components}) set(PCL_FIND_ALL 1) else(PCL_FIND_COMPONENTS_LENGTH EQUAL PCL_NB_COMPONENTS) - set(PCL_TO_FIND_COMPONENTS ${PCL_FIND_COMPONENTS}) + set(PCL_TO_FIND_COMPONENTS ${PCL_FIND_COMPONENTS}) endif(PCL_FIND_COMPONENTS_LENGTH EQUAL PCL_NB_COMPONENTS) else(PCL_FIND_COMPONENTS) set(PCL_TO_FIND_COMPONENTS ${pcl_all_components}) @@ -786,6 +460,21 @@ endif(PCL_FIND_COMPONENTS) compute_dependencies(PCL_TO_FIND_COMPONENTS) +# We do not need to find components that have been found already, e.g. during previous invocation +# of find_package(PCL). Filter them out. +foreach(component ${PCL_TO_FIND_COMPONENTS}) + string(TOUPPER "${component}" COMPONENT) + if(NOT PCL_${COMPONENT}_FOUND) + list(APPEND _PCL_TO_FIND_COMPONENTS ${component}) + endif() +endforeach() +set(PCL_TO_FIND_COMPONENTS ${_PCL_TO_FIND_COMPONENTS}) +unset(_PCL_TO_FIND_COMPONENTS) + +if(NOT PCL_TO_FIND_COMPONENTS) + return() +endif() + # compute external dependencies per component foreach(component ${PCL_TO_FIND_COMPONENTS}) foreach(opt ${pcl_${component}_opt_dep}) @@ -793,7 +482,7 @@ foreach(component ${PCL_TO_FIND_COMPONENTS}) endforeach(opt) foreach(ext ${pcl_${component}_ext_dep}) find_external_library(${component} ${ext} REQUIRED) - endforeach(ext) + endforeach(ext) endforeach(component) foreach(component ${PCL_TO_FIND_COMPONENTS}) @@ -803,8 +492,8 @@ foreach(component ${PCL_TO_FIND_COMPONENTS}) pcl_message(STATUS "looking for PCL_${COMPONENT}") string(REGEX REPLACE "^cuda_(.*)$" "\\1" cuda_component "${component}") - string(REGEX REPLACE "^gpu_(.*)$" "\\1" gpu_component "${component}") - + string(REGEX REPLACE "^gpu_(.*)$" "\\1" gpu_component "${component}") + find_path(PCL_${COMPONENT}_INCLUDE_DIR NAMES pcl/${component} pcl/apps/${component} @@ -815,17 +504,18 @@ foreach(component ${PCL_TO_FIND_COMPONENTS}) PATH_SUFFIXES ${component}/include apps/${component}/include - cuda/${cuda_component}/include + cuda/${cuda_component}/include gpu/${gpu_component}/include DOC "path to ${component} headers" NO_DEFAULT_PATH) + mark_as_advanced(PCL_${COMPONENT}_INCLUDE_DIR) if(PCL_${COMPONENT}_INCLUDE_DIR) list(APPEND PCL_${COMPONENT}_INCLUDE_DIRS "${PCL_${COMPONENT}_INCLUDE_DIR}") else(PCL_${COMPONENT}_INCLUDE_DIR) #pcl_message("No include directory found for pcl_${component}.") endif(PCL_${COMPONENT}_INCLUDE_DIR) - + # Skip find_library for header only modules list(FIND pcl_header_only_components ${component} _is_header_only) if(_is_header_only EQUAL -1) @@ -833,16 +523,19 @@ foreach(component ${PCL_TO_FIND_COMPONENTS}) HINTS ${PCL_LIBRARY_DIRS} DOC "path to ${pcl_component} library" NO_DEFAULT_PATH) - get_filename_component(${component}_library_path + get_filename_component(${component}_library_path ${PCL_${COMPONENT}_LIBRARY} PATH) + mark_as_advanced(PCL_${COMPONENT}_LIBRARY) find_library(PCL_${COMPONENT}_LIBRARY_DEBUG ${pcl_component}${PCL_DEBUG_SUFFIX} - HINTS ${PCL_LIBRARY_DIRS} + HINTS ${PCL_LIBRARY_DIRS} DOC "path to ${pcl_component} library debug" NO_DEFAULT_PATH) + mark_as_advanced(PCL_${COMPONENT}_LIBRARY_DEBUG) + if(PCL_${COMPONENT}_LIBRARY_DEBUG) - get_filename_component(${component}_library_path_debug + get_filename_component(${component}_library_path_debug ${PCL_${COMPONENT}_LIBRARY_DEBUG} PATH) endif(PCL_${COMPONENT}_LIBRARY_DEBUG) @@ -857,37 +550,83 @@ foreach(component ${PCL_TO_FIND_COMPONENTS}) PCL_${COMPONENT}_LIBRARY PCL_${COMPONENT}_INCLUDE_DIR) else(_is_header_only EQUAL -1) find_package_handle_standard_args(PCL_${COMPONENT} DEFAULT_MSG - PCL_${COMPONENT}_INCLUDE_DIR) + PCL_${COMPONENT}_INCLUDE_DIR) endif(_is_header_only EQUAL -1) - + if(PCL_${COMPONENT}_FOUND) if(NOT "${PCL_${COMPONENT}_INCLUDE_DIRS}" STREQUAL "") - list(REMOVE_DUPLICATES PCL_${COMPONENT}_INCLUDE_DIRS) + set(_filtered "") + foreach(_inc ${PCL_${COMPONENT}_INCLUDE_DIRS}) + if(EXISTS ${_inc}) + list(APPEND _filtered "${_inc}") + endif() + endforeach() + list(REMOVE_DUPLICATES _filtered) + set(PCL_${COMPONENT}_INCLUDE_DIRS ${_filtered}) + list(APPEND PCL_INCLUDE_DIRS ${_filtered}) endif(NOT "${PCL_${COMPONENT}_INCLUDE_DIRS}" STREQUAL "") - list(APPEND PCL_INCLUDE_DIRS ${PCL_${COMPONENT}_INCLUDE_DIRS}) mark_as_advanced(PCL_${COMPONENT}_INCLUDE_DIRS) if(_is_header_only EQUAL -1) list(APPEND PCL_DEFINITIONS ${PCL_${COMPONENT}_DEFINITIONS}) list(APPEND PCL_LIBRARY_DIRS ${component_library_path}) if(PCL_${COMPONENT}_LIBRARY_DEBUG) - list(APPEND PCL_${COMPONENT}_LIBRARIES optimized ${PCL_${COMPONENT}_LIBRARY} debug ${PCL_${COMPONENT}_LIBRARY_DEBUG}) list(APPEND PCL_LIBRARY_DIRS ${component_library_path_debug}) - else(PCL_${COMPONENT}_LIBRARY_DEBUG) - list(APPEND PCL_${COMPONENT}_LIBRARIES ${PCL_${COMPONENT}_LIBRARY}) - endif(PCL_${COMPONENT}_LIBRARY_DEBUG) - list(APPEND PCL_LIBRARIES ${PCL_${COMPONENT}_LIBRARIES}) + endif() + list(APPEND PCL_COMPONENTS ${pcl_component}) mark_as_advanced(PCL_${COMPONENT}_LIBRARY PCL_${COMPONENT}_LIBRARY_DEBUG) - endif(_is_header_only EQUAL -1) + endif() # Append internal dependencies foreach(int_dep ${pcl_${component}_int_dep}) string(TOUPPER "${int_dep}" INT_DEP) if(PCL_${INT_DEP}_FOUND) list(APPEND PCL_${COMPONENT}_INCLUDE_DIRS ${PCL_${INT_DEP}_INCLUDE_DIRS}) if(PCL_${INT_DEP}_LIBRARIES) - list(APPEND PCL_${COMPONENT}_LIBRARIES "${PCL_${INT_DEP}_LIBRARIES}") - endif(PCL_${INT_DEP}_LIBRARIES) + list(APPEND PCL_${COMPONENT}_LINK_LIBRARIES "${PCL_${INT_DEP}_LIBRARIES}") + endif(PCL_${INT_DEP}_LIBRARIES) endif(PCL_${INT_DEP}_FOUND) endforeach(int_dep) + if(_is_header_only EQUAL -1) + add_library(${pcl_component} @PCL_LIB_TYPE@ IMPORTED) + if(PCL_${COMPONENT}_LIBRARY_DEBUG) + set_target_properties(${pcl_component} + PROPERTIES + IMPORTED_CONFIGURATIONS "RELEASE;DEBUG" + IMPORTED_LOCATION_RELEASE "${PCL_${COMPONENT}_LIBRARY}" + IMPORTED_LOCATION_DEBUG "${PCL_${COMPONENT}_LIBRARY_DEBUG}" + IMPORTED_IMPLIB_RELEASE "${PCL_${COMPONENT}_LIBRARY}" + IMPORTED_IMPLIB_DEBUG "${PCL_${COMPONENT}_LIBRARY_DEBUG}" + ) + else() + set_target_properties(${pcl_component} + PROPERTIES + IMPORTED_LOCATION "${PCL_${COMPONENT}_LIBRARY}" + IMPORTED_IMPLIB "${PCL_${COMPONENT}_LIBRARY}" + ) + endif() + foreach(def ${PCL_DEFINITIONS}) + string(REPLACE " " ";" def2 ${def}) + string(REGEX REPLACE "^-D" "" def3 "${def2}") + list(APPEND definitions ${def3}) + endforeach() + if(CMAKE_VERSION VERSION_LESS 3.3) + set_target_properties(${pcl_component} + PROPERTIES + INTERFACE_COMPILE_DEFINITIONS "${definitions}" + INTERFACE_COMPILE_OPTIONS "${PCL_COMPILE_OPTIONS}" + INTERFACE_INCLUDE_DIRECTORIES "${PCL_${COMPONENT}_INCLUDE_DIRS}" + INTERFACE_LINK_LIBRARIES "${PCL_${COMPONENT}_LINK_LIBRARIES}" + ) + else() + set_target_properties(${pcl_component} + PROPERTIES + INTERFACE_COMPILE_DEFINITIONS "${definitions}" + INTERFACE_COMPILE_OPTIONS "$<$:${PCL_COMPILE_OPTIONS}>" + INTERFACE_INCLUDE_DIRECTORIES "${PCL_${COMPONENT}_INCLUDE_DIRS}" + INTERFACE_LINK_LIBRARIES "${PCL_${COMPONENT}_LINK_LIBRARIES}" + ) + endif() + set(PCL_${COMPONENT}_LIBRARIES ${pcl_component}) + endif() endif(PCL_${COMPONENT}_FOUND) endforeach(component) @@ -903,11 +642,14 @@ if(NOT "${PCL_DEFINITIONS}" STREQUAL "") list(REMOVE_DUPLICATES PCL_DEFINITIONS) endif(NOT "${PCL_DEFINITIONS}" STREQUAL "") -pcl_remove_duplicate_libraries(PCL_LIBRARIES PCL_DEDUP_LIBRARIES) -set(PCL_LIBRARIES ${PCL_DEDUP_LIBRARIES}) +pcl_remove_duplicate_libraries(PCL_COMPONENTS PCL_LIBRARIES) + # Add 3rd party libraries, as user code might include our .HPP implementations list(APPEND PCL_LIBRARIES ${BOOST_LIBRARIES} ${QHULL_LIBRARIES} ${OPENNI_LIBRARIES} ${OPENNI2_LIBRARIES} ${ENSENSO_LIBRARIES} ${davidSDK_LIBRARIES} ${DSSDK_LIBRARIES} ${RSSDK_LIBRARIES} ${FLANN_LIBRARIES} ${VTK_LIBRARIES}) find_package_handle_standard_args(PCL DEFAULT_MSG PCL_LIBRARIES PCL_INCLUDE_DIRS) mark_as_advanced(PCL_LIBRARIES PCL_INCLUDE_DIRS PCL_LIBRARY_DIRS) +if(POLICY CMP0074) + cmake_policy(POP) +endif() diff --git a/README.md b/README.md index 78ad1947..27e2b127 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,20 @@ # Point Cloud Library - + Continuous integration ---------------------- [![Release][release-image]][releases] [![License][license-image]][license] -[release-image]: https://img.shields.io/badge/release-1.8.0-green.svg?style=flat +[release-image]: https://img.shields.io/badge/release-1.9.0-green.svg?style=flat [releases]: https://github.com/PointCloudLibrary/pcl/releases [license-image]: https://img.shields.io/badge/license-BSD-green.svg?style=flat [license]: https://github.com/PointCloudLibrary/pcl/blob/master/LICENSE.txt -[![Build Status](https://travis-ci.org/PointCloudLibrary/pcl.svg?branch=master)](https://travis-ci.org/PointCloudLibrary/pcl) +[![Build Status](https://travis-ci.com/PointCloudLibrary/pcl.svg?branch=master)](https://travis-ci.com/PointCloudLibrary/pcl) +[![Build Status](https://ci.appveyor.com/api/projects/status/oiep6oktpmuap7qr/branch/master?svg=true)](https://ci.appveyor.com/project/PointCloudLibrary/pcl/branch/master) Description ----------- @@ -31,7 +32,7 @@ Please refer to the platform specific tutorials: Documentation ------------- - [Tutorials](http://www.pointclouds.org/documentation/tutorials/) -- [PCL trunk documentation](http://docs.pointclouds.org/trunk/) (generated 2 times a week) +- [PCL trunk documentation](http://docs.pointclouds.org/trunk/) (updated daily) Contributing ------------ @@ -41,3 +42,7 @@ Issues ------ For general questions on how to use the PCL, please use the [pcl-users](http://www.pcl-users.org/) mailing list (do not forget to subscribe before posting). To report issues, please read [CONTRIBUTING.md#bug-reports](https://github.com/PointCloudLibrary/pcl/blob/master/CONTRIBUTING.md#bug-reports). + +API/ABI Compatibility Report +------ +For details about API/ABI changes over the timeline please check PCL's page at [ABI Laboratory](https://abi-laboratory.pro/tracker/timeline/pcl/). diff --git a/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/feature_wrapper/normal_estimator.h b/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/feature_wrapper/normal_estimator.h index a5229cda..4399dfb0 100644 --- a/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/feature_wrapper/normal_estimator.h +++ b/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/feature_wrapper/normal_estimator.h @@ -140,7 +140,7 @@ namespace pcl if (out->points.size () == 0) { - PCL_WARN("NORMAL estimator: Cloud has no points after voxel grid, wont be able to compute normals!\n"); + PCL_WARN("NORMAL estimator: Cloud has no points after voxel grid, won't be able to compute normals!\n"); return; } diff --git a/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/pipeline/global_nn_recognizer_crh.h b/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/pipeline/global_nn_recognizer_crh.h index 93202b78..80d116dd 100644 --- a/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/pipeline/global_nn_recognizer_crh.h +++ b/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/pipeline/global_nn_recognizer_crh.h @@ -95,8 +95,9 @@ namespace pcl bool use_cache_; - std::map, Eigen::Matrix4f, std::less >, Eigen::aligned_allocator, Eigen::Matrix4f> > > poses_cache_; + std::map, Eigen::Matrix4f, + std::less >, + Eigen::aligned_allocator, Eigen::Matrix4f> > > poses_cache_; std::map, Eigen::Vector3f > centroids_cache_; std::vector indices_; diff --git a/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/pipeline/global_nn_recognizer_cvfh.h b/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/pipeline/global_nn_recognizer_cvfh.h index 5f070047..ddf26ad2 100644 --- a/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/pipeline/global_nn_recognizer_cvfh.h +++ b/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/pipeline/global_nn_recognizer_cvfh.h @@ -138,8 +138,9 @@ namespace pcl bool use_single_categories_; bool use_cache_; - std::map, Eigen::Matrix4f, std::less >, Eigen::aligned_allocator, Eigen::Matrix4f> > > poses_cache_; + std::map, Eigen::Matrix4f, + std::less >, + Eigen::aligned_allocator, Eigen::Matrix4f> > > poses_cache_; std::map, Eigen::Vector3f> centroids_cache_; std::vector indices_; diff --git a/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/pipeline/impl/global_nn_recognizer_crh.hpp b/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/pipeline/impl/global_nn_recognizer_crh.hpp index 84a9ace8..d296e985 100644 --- a/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/pipeline/impl/global_nn_recognizer_crh.hpp +++ b/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/pipeline/impl/global_nn_recognizer_crh.hpp @@ -22,8 +22,9 @@ template class Distance, typename PointInT, typename FeatureT> typedef std::pair mv_pair; mv_pair pair_model_view = std::make_pair (model.id_, view_id); - std::map, Eigen::aligned_allocator > >::iterator it = - poses_cache_.find (pair_model_view); + std::map, + Eigen::aligned_allocator > >::iterator it = poses_cache_.find (pair_model_view); if (it != poses_cache_.end ()) { diff --git a/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/pipeline/impl/global_nn_recognizer_cvfh.hpp b/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/pipeline/impl/global_nn_recognizer_cvfh.hpp index c209976c..f0d93b46 100644 --- a/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/pipeline/impl/global_nn_recognizer_cvfh.hpp +++ b/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/pipeline/impl/global_nn_recognizer_cvfh.hpp @@ -22,8 +22,9 @@ template class Distance, typename PointInT, typename FeatureT> typedef std::pair mv_pair; mv_pair pair_model_view = std::make_pair (model.id_, view_id); - std::map, Eigen::aligned_allocator > >::iterator it = - poses_cache_.find (pair_model_view); + std::map, + Eigen::aligned_allocator > >::iterator it = poses_cache_.find (pair_model_view); if (it != poses_cache_.end ()) { @@ -295,7 +296,7 @@ template class Distance, typename PointInT, typename FeatureT> nearestKSearch (single_categories_index_[it->second], histogram, NN_, indices, distances); //gather NN-search results double score = 0; - for (size_t i = 0; i < NN_; ++i) + for (size_t i = 0; i < (size_t) NN_; ++i) { score = distances[0][i]; index_score is; diff --git a/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/pipeline/impl/local_recognizer.hpp b/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/pipeline/impl/local_recognizer.hpp index 77854bf4..3ccc4a6f 100644 --- a/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/pipeline/impl/local_recognizer.hpp +++ b/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/pipeline/impl/local_recognizer.hpp @@ -463,8 +463,9 @@ template class Distance, typename PointInT, typename FeatureT> typedef std::pair mv_pair; mv_pair pair_model_view = std::make_pair (model.id_, view_id); - std::map, Eigen::aligned_allocator > >::iterator it = - poses_cache_.find (pair_model_view); + std::map, + Eigen::aligned_allocator > >::iterator it = poses_cache_.find (pair_model_view); if (it != poses_cache_.end ()) { diff --git a/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/pipeline/local_recognizer.h b/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/pipeline/local_recognizer.h index b882dd47..0b55f251 100644 --- a/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/pipeline/local_recognizer.h +++ b/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/pipeline/local_recognizer.h @@ -88,8 +88,9 @@ namespace pcl std::vector indices_; bool use_cache_; - std::map, Eigen::Matrix4f, std::less >, Eigen::aligned_allocator, Eigen::Matrix4f> > > poses_cache_; + std::map, Eigen::Matrix4f, + std::less >, + Eigen::aligned_allocator, Eigen::Matrix4f> > > poses_cache_; std::map, typename pcl::PointCloud::Ptr> keypoints_cache_; float threshold_accept_model_hypothesis_; @@ -334,7 +335,7 @@ namespace pcl /** * \brief Initializes the FLANN structure from the provided source - * It does training for the models that havent been trained yet + * It does training for the models that haven't been trained yet */ void diff --git a/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/utils/metrics.h b/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/utils/metrics.h index e5186904..ea8db440 100644 --- a/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/utils/metrics.h +++ b/apps/3d_rec_framework/include/pcl/apps/3d_rec_framework/utils/metrics.h @@ -121,7 +121,7 @@ namespace Metrics */ template inline ResultType - accum_dist (const U& a, const V& b, int dim) const + accum_dist (const U& a, const V& b, int) const { //printf("New code being used, accum_dist\n"); ResultType min0; diff --git a/apps/3d_rec_framework/tools/apps/src/local_recognition_mian_dataset.cpp b/apps/3d_rec_framework/tools/apps/src/local_recognition_mian_dataset.cpp index 4bed483a..9efd874b 100644 --- a/apps/3d_rec_framework/tools/apps/src/local_recognition_mian_dataset.cpp +++ b/apps/3d_rec_framework/tools/apps/src/local_recognition_mian_dataset.cpp @@ -117,7 +117,7 @@ template class DistT, typename PointT, typename FeatureT> { std::cout << files[i] << std::endl; if (scene != -1) - if (scene != i) + if ((size_t) scene != i) continue; std::stringstream file; diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index ff18a65c..67aec271 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -1,6 +1,6 @@ set(SUBSYS_NAME apps) set(SUBSYS_DESC "Application examples/samples that show how PCL works") -set(SUBSYS_DEPS common geometry io filters sample_consensus segmentation visualization kdtree features surface octree registration keypoints tracking search recognition ml stereo) +set(SUBSYS_DEPS common geometry io filters sample_consensus segmentation visualization kdtree features surface octree registration keypoints tracking search recognition ml stereo 2d) set(DEFAULT FALSE) PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ${DEFAULT} "${REASON}") @@ -205,7 +205,9 @@ if(build) # OpenGL and GLUT if(OPENGL_FOUND AND GLUT_FOUND) - include_directories("${OPENGL_INCLUDE_DIR}") + if(NOT WIN32) + include_directories("${OPENGL_INCLUDE_DIR}") + endif() include_directories("${GLUT_INCLUDE_DIR}") PCL_ADD_EXECUTABLE_OPT_BUNDLE(pcl_grabcut_2d "${SUBSYS_NAME}" src/grabcut_2d.cpp) target_link_libraries (pcl_grabcut_2d pcl_common pcl_io pcl_segmentation pcl_search ${GLUT_LIBRARIES} ${OPENGL_LIBRARIES}) diff --git a/apps/cloud_composer/include/pcl/apps/cloud_composer/item_inspector.h b/apps/cloud_composer/include/pcl/apps/cloud_composer/item_inspector.h index c9fb0943..f4527474 100644 --- a/apps/cloud_composer/include/pcl/apps/cloud_composer/item_inspector.h +++ b/apps/cloud_composer/include/pcl/apps/cloud_composer/item_inspector.h @@ -74,7 +74,7 @@ namespace pcl /** \brief Stores the state of the current tree view in item_treestate_map_ */ void storeTreeState (); - /** \brief Retores the state of \param model 's view from item_treestate_map_ */ + /** \brief Restores the state of \param model 's view from item_treestate_map_ */ void restoreTreeState (); /** \brief Removes the extra tabs the item might have */ diff --git a/apps/cloud_composer/include/pcl/apps/cloud_composer/tools/impl/organized_segmentation.hpp b/apps/cloud_composer/include/pcl/apps/cloud_composer/tools/impl/organized_segmentation.hpp index 50295f7b..b3e8e12d 100644 --- a/apps/cloud_composer/include/pcl/apps/cloud_composer/tools/impl/organized_segmentation.hpp +++ b/apps/cloud_composer/include/pcl/apps/cloud_composer/tools/impl/organized_segmentation.hpp @@ -103,18 +103,14 @@ std::vector boundary_indices; mps.segmentAndRefine (regions, model_coefficients, inlier_indices, labels, label_indices, boundary_indices); - std::vector plane_labels; - plane_labels.resize (label_indices.size (), false); - for (size_t i = 0; i < label_indices.size (); i++) - { - if (label_indices[i].indices.size () > min_plane_size) - { - plane_labels[i] = true; - } - } + boost::shared_ptr > plane_labels = boost::make_shared > (); + for (size_t i = 0; i < label_indices.size (); ++i) + if (label_indices[i].indices.size () > (size_t) min_plane_size) + plane_labels->insert (i); typename PointCloud::CloudVectorType clusters; - typename EuclideanClusterComparator::Ptr euclidean_cluster_comparator = boost::make_shared > (); + typename EuclideanClusterComparator::Ptr euclidean_cluster_comparator = + boost::make_shared > (); euclidean_cluster_comparator->setInputCloud (input_cloud); euclidean_cluster_comparator->setLabels (labels); euclidean_cluster_comparator->setExcludeLabels (plane_labels); @@ -129,7 +125,7 @@ pcl::IndicesPtr extracted_indices (new std::vector ()); for (size_t i = 0; i < euclidean_label_indices.size (); i++) { - if (euclidean_label_indices[i].indices.size () >= min_cluster_size) + if (euclidean_label_indices[i].indices.size () >= (size_t) min_cluster_size) { typename PointCloud::Ptr cluster = boost::shared_ptr > (new PointCloud); pcl::copyPointCloud (*input_cloud,euclidean_label_indices[i].indices,*cluster); @@ -144,7 +140,7 @@ for (size_t i = 0; i < label_indices.size (); i++) { - if (label_indices[i].indices.size () >= min_plane_size) + if (label_indices[i].indices.size () >= (size_t) min_plane_size) { typename PointCloud::Ptr plane = boost::shared_ptr > (new PointCloud); pcl::copyPointCloud (*input_cloud,label_indices[i].indices,*plane); diff --git a/apps/cloud_composer/src/commands.cpp b/apps/cloud_composer/src/commands.cpp index ecd6d015..a61274ad 100644 --- a/apps/cloud_composer/src/commands.cpp +++ b/apps/cloud_composer/src/commands.cpp @@ -582,7 +582,7 @@ pcl::cloud_composer::MergeCloudCommand::redo () { qDebug () << "Redo in MergeCloudCommand "; last_was_undo_ = false; - //There is only one output_pair, but thats ok + //There is only one output_pair, but that's ok foreach (OutputPair output_pair, output_data_) { //Replace the input with the output for this pair diff --git a/apps/cloud_composer/src/items/cloud_item.cpp b/apps/cloud_composer/src/items/cloud_item.cpp index d73a1355..b77641ba 100644 --- a/apps/cloud_composer/src/items/cloud_item.cpp +++ b/apps/cloud_composer/src/items/cloud_item.cpp @@ -90,7 +90,7 @@ pcl::cloud_composer::CloudItem::removeFromView (boost::shared_ptr (new pcl::visualization::PCLPlotter); diff --git a/apps/cloud_composer/src/merge_selection.cpp b/apps/cloud_composer/src/merge_selection.cpp index 3638380c..1b80784e 100644 --- a/apps/cloud_composer/src/merge_selection.cpp +++ b/apps/cloud_composer/src/merge_selection.cpp @@ -23,7 +23,7 @@ pcl::cloud_composer::MergeSelection::performAction (ConstItemList input_data, Po { if (type != PointTypeFlags::NONE) { - switch (type) + switch ((uint8_t) type) { case (PointTypeFlags::XYZ): return this->performTemplatedAction (input_data); diff --git a/apps/cloud_composer/src/point_selectors/selection_event.cpp b/apps/cloud_composer/src/point_selectors/selection_event.cpp index 4f80fda6..4f1e295c 100644 --- a/apps/cloud_composer/src/point_selectors/selection_event.cpp +++ b/apps/cloud_composer/src/point_selectors/selection_event.cpp @@ -11,7 +11,7 @@ pcl::cloud_composer::SelectionEvent::~SelectionEvent () void pcl::cloud_composer::SelectionEvent::findIndicesInItem (CloudItem* cloud_item, pcl::PointIndices::Ptr indices) { - // WE DONT NEED TO DO THIS SEARCH BECAUSE WE HAVE A 1-1 CORRESPONDENCE VTK TO PCL + // WE DON'T NEED TO DO THIS SEARCH BECAUSE WE HAVE A 1-1 CORRESPONDENCE VTK TO PCL // THIS IS ONLY THE CASE FOR CLOUDS WITH NO NANs //Go through every point in the selected data set and find the matching index in this cloud //pcl::search::KdTree::Ptr search = cloud_item->data (KD_TREE_SEARCH).value ::Ptr> (); diff --git a/apps/cloud_composer/src/project_model.cpp b/apps/cloud_composer/src/project_model.cpp index 23ccb9f7..70e26230 100644 --- a/apps/cloud_composer/src/project_model.cpp +++ b/apps/cloud_composer/src/project_model.cpp @@ -292,9 +292,9 @@ pcl::cloud_composer::ProjectModel::insertNewCloudFromRGBandDepth () depth_pixel = static_cast(depth_image->GetScalarPointer (depth_dims[0]-1,depth_dims[1]-1,0)); color_pixel = static_cast (rgb_image->GetScalarPointer (depth_dims[0]-1,depth_dims[1]-1,0)); - for (int y=0; yheight; ++y) + for (uint32_t y=0; yheight; ++y) { - for (int x=0; xwidth; ++x, --depth_pixel, color_pixel-=3) + for (uint32_t x=0; xwidth; ++x, --depth_pixel, color_pixel-=3) { PointXYZRGB new_point; // uint8_t* p_i = &(cloud_blob->data[y * cloud_blob->row_step + x * cloud_blob->point_step]); diff --git a/apps/cloud_composer/src/properties_model.cpp b/apps/cloud_composer/src/properties_model.cpp index 3688caa1..c3397e42 100644 --- a/apps/cloud_composer/src/properties_model.cpp +++ b/apps/cloud_composer/src/properties_model.cpp @@ -111,7 +111,6 @@ pcl::cloud_composer::PropertiesModel::copyProperties (const PropertiesModel* to_ for (int i=0; i < to_copy->rowCount (); ++i){ QList new_row; QStandardItem* parent = to_copy->item(i,0); - QModelIndex parent_index = to_copy->index(i,0); qDebug () << "Copying "<text()<< " cols ="<columnCount (); new_row.append (parent->clone ()); for (int j=1; j < to_copy->columnCount (); ++j) diff --git a/apps/cloud_composer/src/transform_clouds.cpp b/apps/cloud_composer/src/transform_clouds.cpp index e893084d..22c2197b 100644 --- a/apps/cloud_composer/src/transform_clouds.cpp +++ b/apps/cloud_composer/src/transform_clouds.cpp @@ -23,7 +23,7 @@ pcl::cloud_composer::TransformClouds::performAction (ConstItemList input_data, P { if (type != PointTypeFlags::NONE) { - switch (type) + switch ((uint8_t) type) { case (PointTypeFlags::XYZ): return this->performTemplatedAction (input_data); diff --git a/apps/cloud_composer/tools/organized_segmentation.cpp b/apps/cloud_composer/tools/organized_segmentation.cpp index 716bba12..a374e75d 100644 --- a/apps/cloud_composer/tools/organized_segmentation.cpp +++ b/apps/cloud_composer/tools/organized_segmentation.cpp @@ -31,7 +31,7 @@ pcl::cloud_composer::OrganizedSegmentationTool::performAction (ConstItemList inp { if (type != PointTypeFlags::NONE) { - switch (type) + switch ((uint8_t) type) { case (PointTypeFlags::XYZ): return this->performTemplatedAction (input_data); diff --git a/apps/cloud_composer/tools/supervoxels.cpp b/apps/cloud_composer/tools/supervoxels.cpp index 92b0781a..e0b77633 100644 --- a/apps/cloud_composer/tools/supervoxels.cpp +++ b/apps/cloud_composer/tools/supervoxels.cpp @@ -29,7 +29,7 @@ pcl::cloud_composer::SupervoxelsTool::performAction (ConstItemList input_data, P { if (type != PointTypeFlags::NONE) { - switch (type) + switch ((uint8_t) type) { case (PointTypeFlags::XYZ | PointTypeFlags::RGB): return this->performTemplatedAction (input_data); diff --git a/apps/in_hand_scanner/src/icp.cpp b/apps/in_hand_scanner/src/icp.cpp index 6bbfad3b..ffac8ca5 100644 --- a/apps/in_hand_scanner/src/icp.cpp +++ b/apps/in_hand_scanner/src/icp.cpp @@ -244,7 +244,7 @@ pcl::ihs::ICP::findTransformation (const MeshConstPtr& mesh_model, // Check the distance threshold if (squared_distance [0] < squared_distance_threshold) { - if (index [0] >= cloud_model_selected->size ()) + if ((size_t) index [0] >= cloud_model_selected->size ()) { std::cerr << "ERROR in icp.cpp: Segfault!\n"; std::cerr << " Trying to access index " << index [0] << " >= " << cloud_model_selected->size () << std::endl; diff --git a/apps/in_hand_scanner/src/mesh_processing.cpp b/apps/in_hand_scanner/src/mesh_processing.cpp index 1c4213ef..7ec8ad86 100644 --- a/apps/in_hand_scanner/src/mesh_processing.cpp +++ b/apps/in_hand_scanner/src/mesh_processing.cpp @@ -93,7 +93,7 @@ pcl::ihs::MeshProcessing::processBoundary (Mesh& mesh, const std::vector ::Ptr rgb_comparator_; pcl::RGBPlaneCoefficientComparator rgb_comp_; pcl::EdgeAwarePlaneComparator::Ptr edge_aware_comparator_; - pcl::EuclideanClusterComparator::Ptr euclidean_cluster_comparator_; + pcl::EuclideanClusterComparator::Ptr euclidean_cluster_comparator_; public Q_SLOTS: void toggleCapturePressed() diff --git a/apps/include/pcl/apps/pcd_video_player.h b/apps/include/pcl/apps/pcd_video_player.h index 3d395c87..59afa0b5 100644 --- a/apps/include/pcl/apps/pcd_video_player.h +++ b/apps/include/pcl/apps/pcd_video_player.h @@ -128,7 +128,7 @@ class PCDVideoPlayer : public QMainWindow /** \brief Indicate that the timeoutSlot needs to reload the pointcloud */ bool cloud_modified_; - /** \brief Indicate that files should play continiously */ + /** \brief Indicate that files should play continuously */ bool play_mode_; /** \brief In play mode only update if speed_counter_ == speed_value */ unsigned int speed_counter_; diff --git a/apps/include/pcl/apps/render_views_tesselated_sphere.h b/apps/include/pcl/apps/render_views_tesselated_sphere.h index 04a18d55..a311046e 100644 --- a/apps/include/pcl/apps/render_views_tesselated_sphere.h +++ b/apps/include/pcl/apps/render_views_tesselated_sphere.h @@ -17,7 +17,7 @@ namespace pcl { namespace apps { - /** \brief @b Class to render synthetic views of a 3D mesh using a tesselated sphere + /** \brief @b Class to render synthetic views of a 3D mesh using a tessellated sphere * NOTE: This class should replace renderViewTesselatedSphere from pcl::visualization. * Some extensions are planned in the near future to this class like removal of duplicated views for * symmetrical objects, generation of RGB synthetic clouds when RGB available on mesh, etc. @@ -70,7 +70,7 @@ namespace pcl campos_constraints_func_ = bb; } - /* \brief Indicates wether to generate organized or unorganized data + /* \brief Indicates whether to generate organized or unorganized data * \param b organized/unorganized */ void @@ -88,7 +88,7 @@ namespace pcl resolution_ = res; } - /* \brief Wether to use the vertices or triangle centers of the tesselated sphere + /* \brief Whether to use the vertices or triangle centers of the tessellated sphere * \param use true indicates to use vertices, false triangle centers */ @@ -107,7 +107,7 @@ namespace pcl radius_sphere_ = radius; } - /* \brief Wether to compute the entropies (level of occlusions for each view) + /* \brief Whether to compute the entropies (level of occlusions for each view) * \param compute true to compute entropies, false otherwise */ void @@ -116,8 +116,8 @@ namespace pcl compute_entropy_ = compute; } - /* \brief How many times the icosahedron should be tesselated. Results in more or less camera positions and generated views. - * \param level amount of tesselation + /* \brief How many times the icosahedron should be tessellated. Results in more or less camera positions and generated views. + * \param level amount of tessellation */ void setTesselationLevel (int level) diff --git a/apps/modeler/src/parameter_dialog.cpp b/apps/modeler/src/parameter_dialog.cpp index 90d0762c..0dce6d7a 100755 --- a/apps/modeler/src/parameter_dialog.cpp +++ b/apps/modeler/src/parameter_dialog.cpp @@ -156,7 +156,7 @@ pcl::modeler::Parameter* pcl::modeler::ParameterDelegate::getCurrentParameter(co std::map::iterator currentParameter = parameter_map_.begin(); size_t currentRow = 0; - while(currentRow < index.row() && currentParameter != parameter_map_.end()) { + while(currentRow < (size_t) index.row() && currentParameter != parameter_map_.end()) { ++ currentParameter; ++ currentRow; } diff --git a/apps/optronic_viewer/include/pcl/apps/optronic_viewer/filter_window.h b/apps/optronic_viewer/include/pcl/apps/optronic_viewer/filter_window.h index da53c2ad..d92618c0 100644 --- a/apps/optronic_viewer/include/pcl/apps/optronic_viewer/filter_window.h +++ b/apps/optronic_viewer/include/pcl/apps/optronic_viewer/filter_window.h @@ -81,7 +81,7 @@ namespace pcl virtual void next (); Q_SIGNALS: - /** \brief Ommitted when a filter is created. */ + /** \brief Omitted when a filter is created. */ void filterCreated (); protected: diff --git a/apps/point_cloud_editor/include/pcl/apps/point_cloud_editor/cloud.h b/apps/point_cloud_editor/include/pcl/apps/point_cloud_editor/cloud.h index 48dfffcb..654cd3a7 100644 --- a/apps/point_cloud_editor/include/pcl/apps/point_cloud_editor/cloud.h +++ b/apps/point_cloud_editor/include/pcl/apps/point_cloud_editor/cloud.h @@ -49,6 +49,10 @@ # include # include #else +#if _WIN32 +// Need this to pull in APIENTRY, etc. +#include "windows.h" +#endif # include # include #endif @@ -409,7 +413,7 @@ class Cloud : public Statistics /// The internal representation of the cloud Cloud3D cloud_; - /// @breif A weak pointer pointing to the selection object. + /// @brief A weak pointer pointing to the selection object. /// @details This implementation uses the weak pointer to allow for a lazy /// update of the cloud if the selection object is destroyed. boost::weak_ptr selection_wk_ptr_; @@ -418,10 +422,6 @@ class Cloud : public Statistics /// (false) when displaying the cloud bool use_color_ramp_; - /// Flag that indicates whether the cloud should be colored with its own - /// color - bool use_native_color_; - /// The axis which the color ramp is to be applied when drawing the cloud Axis color_ramp_axis_; diff --git a/apps/point_cloud_editor/include/pcl/apps/point_cloud_editor/cloudEditorWidget.h b/apps/point_cloud_editor/include/pcl/apps/point_cloud_editor/cloudEditorWidget.h index a72c293d..32e6f5ce 100644 --- a/apps/point_cloud_editor/include/pcl/apps/point_cloud_editor/cloudEditorWidget.h +++ b/apps/point_cloud_editor/include/pcl/apps/point_cloud_editor/cloudEditorWidget.h @@ -123,7 +123,7 @@ class CloudEditorWidget : public QGLWidget void cut (); - /// @brief Enters the mode where users are able to translate the selecte + /// @brief Enters the mode where users are able to translate the selected /// points. void transform (); diff --git a/apps/point_cloud_editor/include/pcl/apps/point_cloud_editor/commandQueue.h b/apps/point_cloud_editor/include/pcl/apps/point_cloud_editor/commandQueue.h index 199afadc..2d49090d 100644 --- a/apps/point_cloud_editor/include/pcl/apps/point_cloud_editor/commandQueue.h +++ b/apps/point_cloud_editor/include/pcl/apps/point_cloud_editor/commandQueue.h @@ -53,7 +53,7 @@ class CommandQueue { public: - /// @brief Defaut Constructor + /// @brief Default Constructor /// @details Creates a command queue object and makes its depth limit /// be the default value. CommandQueue (); @@ -76,12 +76,12 @@ class CommandQueue void execute (CommandPtr); - /// @brief Undoes the last command by poping the tail of the queue, invoke + /// @brief Undoes the last command by popping the tail of the queue, invoke /// the undo function of the command. void undo (); - /// @breif Changes the command history limit. + /// @brief Changes the command history limit. /// @details If the passed size is smaller than the current size then the /// oldest commands are removed (their undo functions are not called). /// @param size The new maximum number of commands that may exist in this diff --git a/apps/point_cloud_editor/include/pcl/apps/point_cloud_editor/transformCommand.h b/apps/point_cloud_editor/include/pcl/apps/point_cloud_editor/transformCommand.h index 12e65612..b710250a 100644 --- a/apps/point_cloud_editor/include/pcl/apps/point_cloud_editor/transformCommand.h +++ b/apps/point_cloud_editor/include/pcl/apps/point_cloud_editor/transformCommand.h @@ -34,7 +34,7 @@ /// /// @file transformCommand.h -/// @details a TransfromCommand object provides transformation and undo +/// @details a TransformCommand object provides transformation and undo /// functionalities. // XXX - transformation of what? /// @author Yue Li and Matthew Hielsberg diff --git a/apps/point_cloud_editor/src/cloud.cpp b/apps/point_cloud_editor/src/cloud.cpp index e330eeaa..f55d90a5 100644 --- a/apps/point_cloud_editor/src/cloud.cpp +++ b/apps/point_cloud_editor/src/cloud.cpp @@ -102,12 +102,12 @@ Cloud::Cloud (const Cloud ©) use_color_ramp_(copy.use_color_ramp_), color_ramp_axis_(copy.color_ramp_axis_), display_scale_(copy.display_scale_), + partitioned_indices_(copy.partitioned_indices_), point_size_(copy.point_size_), selected_point_size_(copy.selected_point_size_), select_translate_x_(copy.select_translate_x_), select_translate_y_(copy.select_translate_y_), - select_translate_z_(copy.select_translate_z_), - partitioned_indices_(copy.partitioned_indices_) + select_translate_z_(copy.select_translate_z_) { std::copy(copy.center_xyz_, copy.center_xyz_+XYZ_SIZE, center_xyz_); std::copy(copy.cloud_matrix_, copy.cloud_matrix_+MATRIX_SIZE, cloud_matrix_); diff --git a/apps/point_cloud_editor/src/cloudEditorWidget.cpp b/apps/point_cloud_editor/src/cloudEditorWidget.cpp index c6a90a7e..505888dc 100644 --- a/apps/point_cloud_editor/src/cloudEditorWidget.cpp +++ b/apps/point_cloud_editor/src/cloudEditorWidget.cpp @@ -306,6 +306,11 @@ CloudEditorWidget::denoise () return; DenoiseParameterForm form; form.exec(); + // check for cancel. + if (!form.ok()) + { + return; + } boost::shared_ptr c(new DenoiseCommand(selection_ptr_, cloud_ptr_, form.getMeanK(), form.getStdDevThresh())); command_queue_ptr_->execute(c); diff --git a/apps/point_cloud_editor/src/cloudTransformTool.cpp b/apps/point_cloud_editor/src/cloudTransformTool.cpp index ba1f44eb..2f07e4ce 100644 --- a/apps/point_cloud_editor/src/cloudTransformTool.cpp +++ b/apps/point_cloud_editor/src/cloudTransformTool.cpp @@ -43,8 +43,6 @@ #include #include -const float DEG_2_RADS = M_PI / 180.0f; - const float CloudTransformTool::DEFAULT_SCALE_FACTOR_ = 1.14; const float CloudTransformTool::DEFAULT_TRANSLATE_FACTOR_ = 0.001f; diff --git a/apps/point_cloud_editor/src/common.cpp b/apps/point_cloud_editor/src/common.cpp index 7289664b..08c79e67 100644 --- a/apps/point_cloud_editor/src/common.cpp +++ b/apps/point_cloud_editor/src/common.cpp @@ -70,7 +70,7 @@ multMatrix(const float* left, const float* right, float* result) // This code was found on: // http://stackoverflow.com/questions/1148309/inverting-a-4x4-matrix -// and is listed as being part of an open soure project (the MESA project) +// and is listed as being part of an open source project (the MESA project) // // The original code in MESA comes from __gluInvertMatrixd() in project.c // diff --git a/apps/point_cloud_editor/src/selectionTransformTool.cpp b/apps/point_cloud_editor/src/selectionTransformTool.cpp index 41148aab..b299d8cc 100644 --- a/apps/point_cloud_editor/src/selectionTransformTool.cpp +++ b/apps/point_cloud_editor/src/selectionTransformTool.cpp @@ -195,13 +195,15 @@ SelectionTransformTool::findSelectionCenter () float min_xyz[XYZ_SIZE] = {0.0f}; float max_xyz[XYZ_SIZE] = {0.0f}; Selection::const_iterator it = selection_ptr_->begin(); - float *pt = &((cloud_ptr_->getObjectSpacePoint(*it)).data[X]); + Point3D point_3d = cloud_ptr_->getObjectSpacePoint (*it); + float *pt = &(point_3d.data[X]); std::copy(pt, pt+XYZ_SIZE, max_xyz); std::copy(max_xyz, max_xyz+XYZ_SIZE, min_xyz); for (++it; it != selection_ptr_->end(); ++it) { - pt = &((cloud_ptr_->getObjectSpacePoint(*it)).data[X]); + Point3D point_3d = cloud_ptr_->getObjectSpacePoint (*it); + pt = &(point_3d.data[X]); for (unsigned int j = 0; j < XYZ_SIZE; ++j) { min_xyz[j] = std::min(min_xyz[j], pt[j]); diff --git a/apps/src/ni_linemod.cpp b/apps/src/ni_linemod.cpp index 9e436a2b..bc84a7ad 100644 --- a/apps/src/ni_linemod.cpp +++ b/apps/src/ni_linemod.cpp @@ -249,7 +249,7 @@ class NILinemod exppd.segment (*points_above_plane); // Use an organized clustering segmentation to extract the individual clusters - EuclideanClusterComparator::Ptr euclidean_cluster_comparator (new EuclideanClusterComparator); + EuclideanClusterComparator::Ptr euclidean_cluster_comparator (new EuclideanClusterComparator); euclidean_cluster_comparator->setInputCloud (cloud); euclidean_cluster_comparator->setDistanceThreshold (0.03f, false); // Set the entire scene to false, and the inliers of the objects located on top of the plane to true @@ -260,8 +260,8 @@ class NILinemod scene->points[points_above_plane->indices[i]].label = 1; euclidean_cluster_comparator->setLabels (scene); - vector exclude_labels (2); exclude_labels[0] = true; exclude_labels[1] = false; - euclidean_cluster_comparator->setExcludeLabels (exclude_labels); + boost::shared_ptr > exclude_labels = boost::make_shared > (); + exclude_labels->insert (0); OrganizedConnectedComponentSegmentation euclidean_segmentation (euclidean_cluster_comparator); euclidean_segmentation.setInputCloud (cloud); diff --git a/apps/src/openni_mls_smoothing.cpp b/apps/src/openni_mls_smoothing.cpp index 256f1e1c..bb3527be 100644 --- a/apps/src/openni_mls_smoothing.cpp +++ b/apps/src/openni_mls_smoothing.cpp @@ -60,8 +60,7 @@ do \ }while(false) -int default_polynomial_order = 2; -bool default_use_polynomial_fit = false; +int default_polynomial_order = 0; double default_search_radius = 0.0, default_sqr_gauss_param = 0.0; @@ -90,15 +89,13 @@ class OpenNISmoothing typedef typename Cloud::ConstPtr CloudConstPtr; OpenNISmoothing (double search_radius, bool sqr_gauss_param_set, double sqr_gauss_param, - bool use_polynomial_fit, int polynomial_order, - const std::string& device_id = "") + int polynomial_order, const std::string& device_id = "") : viewer ("PCL OpenNI MLS Smoothing") , device_id_(device_id) { // Start 4 threads smoother_.setSearchRadius (search_radius); if (sqr_gauss_param_set) smoother_.setSqrGaussParam (sqr_gauss_param); - smoother_.setPolynomialFit (use_polynomial_fit); smoother_.setPolynomialOrder (polynomial_order); typename pcl::search::KdTree::Ptr tree (new typename pcl::search::KdTree ()); @@ -179,8 +176,7 @@ usage (char ** argv) << "where options are:\n" << " -search_radius X = sphere radius to be used for finding the k-nearest neighbors used for fitting (default: " << default_search_radius << ")\n" << " -sqr_gauss_param X = parameter used for the distance based weighting of neighbors (recommended = search_radius^2) (default: " << default_sqr_gauss_param << ")\n" - << " -use_polynomial_fit X = decides whether the surface and normal are approximated using a polynomial or only via tangent estimation (default: " << default_use_polynomial_fit << ")\n" - << " -polynomial_order X = order of the polynomial to be fit (implicitly, use_polynomial_fit = 1) (default: " << default_polynomial_order << ")\n"; + << " -polynomial_order X = order of the polynomial to be fit (0 means tangent estimation) (default: " << default_polynomial_order << ")\n"; openni_wrapper::OpenNIDriver& driver = openni_wrapper::OpenNIDriver::getInstance (); if (driver.getNumberDevices () > 0) @@ -220,26 +216,23 @@ main (int argc, char ** argv) double sqr_gauss_param = default_sqr_gauss_param; bool sqr_gauss_param_set = true; int polynomial_order = default_polynomial_order; - bool use_polynomial_fit = default_use_polynomial_fit; pcl::console::parse_argument (argc, argv, "-search_radius", search_radius); if (pcl::console::parse_argument (argc, argv, "-sqr_gauss_param", sqr_gauss_param) == -1) sqr_gauss_param_set = false; - if (pcl::console::parse_argument (argc, argv, "-polynomial_order", polynomial_order) == -1 ) - use_polynomial_fit = true; - pcl::console::parse_argument (argc, argv, "-use_polynomial_fit", use_polynomial_fit); + pcl::console::parse_argument (argc, argv, "-polynomial_order", polynomial_order); pcl::OpenNIGrabber grabber (arg); if (grabber.providesCallback ()) { OpenNISmoothing v (search_radius, sqr_gauss_param_set, sqr_gauss_param, - use_polynomial_fit, polynomial_order, arg); + polynomial_order, arg); v.run (); } else { OpenNISmoothing v (search_radius, sqr_gauss_param_set, sqr_gauss_param, - use_polynomial_fit, polynomial_order, arg); + polynomial_order, arg); v.run (); } diff --git a/apps/src/organized_segmentation_demo.cpp b/apps/src/organized_segmentation_demo.cpp index 6f499c6c..6d4df480 100644 --- a/apps/src/organized_segmentation_demo.cpp +++ b/apps/src/organized_segmentation_demo.cpp @@ -254,7 +254,7 @@ OrganizedSegmentationDemo::OrganizedSegmentationDemo (pcl::Grabber& grabber) : g euclidean_comparator_.reset (new pcl::EuclideanPlaneCoefficientComparator ()); rgb_comparator_.reset (new pcl::RGBPlaneCoefficientComparator ()); edge_aware_comparator_.reset (new pcl::EdgeAwarePlaneComparator ()); - euclidean_cluster_comparator_ = pcl::EuclideanClusterComparator::Ptr (new pcl::EuclideanClusterComparator ()); + euclidean_cluster_comparator_ = pcl::EuclideanClusterComparator::Ptr (new pcl::EuclideanClusterComparator ()); // Set up Organized Multi Plane Segmentation mps.setMinInliers (10000); @@ -309,15 +309,10 @@ OrganizedSegmentationDemo::cloud_cb (const CloudConstPtr& cloud) if (use_clustering_ && regions.size () > 0) { - std::vector plane_labels; - plane_labels.resize (label_indices.size (), false); - for (size_t i = 0; i < label_indices.size (); i++) - { + boost::shared_ptr > plane_labels = boost::make_shared > (); + for (size_t i = 0; i < label_indices.size (); ++i) if (label_indices[i].indices.size () > 10000) - { - plane_labels[i] = true; - } - } + plane_labels->insert (i); euclidean_cluster_comparator_->setInputCloud (cloud); euclidean_cluster_comparator_->setLabels (labels); diff --git a/apps/src/pcd_select_object_plane.cpp b/apps/src/pcd_select_object_plane.cpp index e7c527cf..0ea6215c 100644 --- a/apps/src/pcd_select_object_plane.cpp +++ b/apps/src/pcd_select_object_plane.cpp @@ -194,7 +194,7 @@ class ObjectSelection if (cloud_->isOrganized ()) { // Use an organized clustering segmentation to extract the individual clusters - typename EuclideanClusterComparator::Ptr euclidean_cluster_comparator (new EuclideanClusterComparator); + typename EuclideanClusterComparator::Ptr euclidean_cluster_comparator (new EuclideanClusterComparator); euclidean_cluster_comparator->setInputCloud (cloud); euclidean_cluster_comparator->setDistanceThreshold (0.03f, false); // Set the entire scene to false, and the inliers of the objects located on top of the plane to true @@ -205,7 +205,8 @@ class ObjectSelection scene->points[points_above_plane->indices[i]].label = 1; euclidean_cluster_comparator->setLabels (scene); - vector exclude_labels (2); exclude_labels[0] = true; exclude_labels[1] = false; + boost::shared_ptr > exclude_labels = boost::make_shared > (); + exclude_labels->insert (0); euclidean_cluster_comparator->setExcludeLabels (exclude_labels); OrganizedConnectedComponentSegmentation euclidean_segmentation (euclidean_cluster_comparator); diff --git a/apps/src/pcd_video_player/pcd_video_player.cpp b/apps/src/pcd_video_player/pcd_video_player.cpp index e990461f..c9b91ad0 100644 --- a/apps/src/pcd_video_player/pcd_video_player.cpp +++ b/apps/src/pcd_video_player/pcd_video_player.cpp @@ -113,7 +113,7 @@ PCDVideoPlayer::PCDVideoPlayer () void PCDVideoPlayer::backButtonPressed () { - if(current_frame_ == 0) // Allready in the beginning + if(current_frame_ == 0) // Already in the beginning { PCL_DEBUG ("[PCDVideoPlayer::nextButtonPressed] : reached the end\n"); current_frame_ = nr_of_frames_ - 1; // reset to end @@ -295,7 +295,7 @@ print_usage () PCL_INFO ("\t Up/Down move a vertical slider by one single step.\n"); PCL_INFO ("\t PageUp moves up one page.\n"); PCL_INFO ("\t PageDown moves down one page.\n"); - PCL_INFO ("\t Home moves to the start (mininum).\n"); + PCL_INFO ("\t Home moves to the start (minimum).\n"); PCL_INFO ("\t End moves to the end (maximum).\n"); } diff --git a/apps/src/render_views_tesselated_sphere.cpp b/apps/src/render_views_tesselated_sphere.cpp index b3e0004f..ea077fb1 100644 --- a/apps/src/render_views_tesselated_sphere.cpp +++ b/apps/src/render_views_tesselated_sphere.cpp @@ -113,7 +113,7 @@ pcl::apps::RenderViewsTesselatedSphere::generateViews() { ico->SetSolidTypeToIcosahedron (); ico->Update (); - //tesselate cells from icosahedron + //tessellate cells from icosahedron vtkSmartPointer subdivide = vtkSmartPointer::New (); subdivide->SetNumberOfSubdivisions (tesselation_level_); subdivide->SetInputConnection (ico->GetOutputPort ()); diff --git a/apps/src/stereo_ground_segmentation.cpp b/apps/src/stereo_ground_segmentation.cpp index e14bbf68..855eab5d 100755 --- a/apps/src/stereo_ground_segmentation.cpp +++ b/apps/src/stereo_ground_segmentation.cpp @@ -393,18 +393,12 @@ class HRCSSegmentation pcl::PointCloud::CloudVectorType clusters; if (ground_cloud->points.size () > 0) { - std::vector plane_labels; - plane_labels.resize (region_indices.size (), false); - for (size_t i = 0; i < region_indices.size (); i++) - { - if (region_indices[i].indices.size () > mps.getMinInliers ()) - { - plane_labels[i] = true; - } - } + boost::shared_ptr > plane_labels = boost::make_shared > (); + for (size_t i = 0; i < region_indices.size (); ++i) + if ((region_indices[i].indices.size () > mps.getMinInliers ())) + plane_labels->insert (i); - pcl::EuclideanClusterComparator::Ptr euclidean_cluster_comparator_ (new pcl::EuclideanClusterComparator ()); - + pcl::EuclideanClusterComparator::Ptr euclidean_cluster_comparator_ (new pcl::EuclideanClusterComparator ()); euclidean_cluster_comparator_->setInputCloud (cloud); euclidean_cluster_comparator_->setLabels (labels_ptr); euclidean_cluster_comparator_->setExcludeLabels (plane_labels); diff --git a/cmake/CMakeParseArguments.cmake b/cmake/CMakeParseArguments.cmake index 7ce4c49a..66016cb2 100644 --- a/cmake/CMakeParseArguments.cmake +++ b/cmake/CMakeParseArguments.cmake @@ -58,7 +58,7 @@ # the new option. # E.g. my_install(TARGETS foo DESTINATION OPTIONAL) would result in # MY_INSTALL_DESTINATION set to "OPTIONAL", but MY_INSTALL_DESTINATION would -# be empty and MY_INSTALL_OPTIONAL would be set to TRUE therefor. +# be empty and MY_INSTALL_OPTIONAL would be set to TRUE therefore. #============================================================================= # Copyright 2010 Alexander Neundorf diff --git a/cmake/Modules/FindEnsenso.cmake b/cmake/Modules/FindEnsenso.cmake index c256328b..98b56015 100644 --- a/cmake/Modules/FindEnsenso.cmake +++ b/cmake/Modules/FindEnsenso.cmake @@ -1,35 +1,48 @@ ############################################################################### -# - Try to find Ensenso SDK (IDS-Imaging) -# Once done this will define -# ENSENSO_FOUND - System has Ensenso SDK -# ENSENSO_INCLUDE_DIRS - The Ensenso SDK include directories -# ENSENSO_LIBRARIES - The libraries needed to use Ensenso SDK -# ENSENSO_DEFINITIONS - Compiler switches required for using Ensenso SDK -# ----------------------- +# Find Ensenso SDK (IDS-Imaging) +# +# find_package(Ensenso) +# +# Variables defined by this module: +# +# ENSENSO_FOUND True if Ensenso SDK was found +# ENSENSO_INCLUDE_DIRS The location(s) of Ensenso SDK headers +# ENSENSO_LIBRARIES Libraries needed to use Ensenso SDK find_path(ENSENSO_INCLUDE_DIR nxLib.h - HINTS ${ENSENSO_ABI_HINT} - /opt/ensenso/development/c - "$ENV{PROGRAMFILES}/Ensenso/development/c" "$ENV{PROGRAMW6432}/Ensenso/development/c" + HINTS "${ENSENSO_ABI_HINT}" + "/opt/ensenso/development/c" + "$ENV{PROGRAMFILES}/Ensenso/development/c" + "$ENV{PROGRAMW6432}/Ensenso/development/c" PATH_SUFFIXES include/) -find_library(ENSENSO_LIBRARY QUIET NAMES NxLib64 NxLib32 nxLib64 nxLib32 - HINTS ${ENSENSO_ABI_HINT} - "$ENV{PROGRAMFILES}/Ensenso/development/c" "$ENV{PROGRAMW6432}/Ensenso/development/c" +find_library(ENSENSO_LIBRARY QUIET + NAMES NxLib64 NxLib32 nxLib64 nxLib32 + HINTS "${ENSENSO_ABI_HINT}" + "$ENV{PROGRAMFILES}/Ensenso/development/c" + "$ENV{PROGRAMW6432}/Ensenso/development/c" PATH_SUFFIXES lib/) -set(ENSENSO_LIBRARIES ${ENSENSO_LIBRARY}) -set(ENSENSO_INCLUDE_DIRS ${ENSENSO_INCLUDE_DIR}) +if(ENSENSO_INCLUDE_DIR AND ENSENSO_LIBRARY) -include(FindPackageHandleStandardArgs) -# handle the QUIETLY and REQUIRED arguments and set ENSENSO_FOUND to TRUE -# if all listed variables are TRUE -find_package_handle_standard_args(ensenso DEFAULT_MSG - ENSENSO_LIBRARY ENSENSO_INCLUDE_DIR) + # Include directories + set(ENSENSO_INCLUDE_DIRS ${ENSENSO_INCLUDE_DIR}) + unset(ENSENSO_INCLUDE_DIR) + mark_as_advanced(ENSENSO_INCLUDE_DIRS) -mark_as_advanced(ENSENSO_INCLUDE_DIR ENSENSO_LIBRARY) + # Libraries + set(ENSENSO_LIBRARIES ${ENSENSO_LIBRARY}) + unset(ENSENSO_LIBRARY) + mark_as_advanced(ENSENSO_LIBRARIES) -if(ENSENSO_FOUND) - message(STATUS "Ensenso SDK found") -endif(ENSENSO_FOUND) +endif() +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(ENSENSO + FOUND_VAR ENSENSO_FOUND + REQUIRED_VARS ENSENSO_LIBRARIES ENSENSO_INCLUDE_DIRS +) + +if(ENSENSO_FOUND) + message(STATUS "Ensenso found (include: ${ENSENSO_INCLUDE_DIRS}, lib: ${ENSENSO_LIBRARIES})") +endif() diff --git a/cmake/Modules/FindFLANN.cmake b/cmake/Modules/FindFLANN.cmake index b5739dc9..1cecacfa 100644 --- a/cmake/Modules/FindFLANN.cmake +++ b/cmake/Modules/FindFLANN.cmake @@ -19,41 +19,41 @@ endif(FLANN_USE_STATIC) find_package(PkgConfig QUIET) if (FLANN_FIND_VERSION) - pkg_check_modules(PC_FLANN flann>=${FLANN_FIND_VERSION}) + pkg_check_modules(FLANN flann>=${FLANN_FIND_VERSION}) else(FLANN_FIND_VERSION) - pkg_check_modules(PC_FLANN flann) + pkg_check_modules(FLANN flann) endif(FLANN_FIND_VERSION) -set(FLANN_DEFINITIONS ${PC_FLANN_CFLAGS_OTHER}) +if(NOT FLANN_FOUND) + find_path(FLANN_INCLUDE_DIR flann/flann.hpp + HINTS "${FLANN_ROOT}" "$ENV{FLANN_ROOT}" + PATHS "$ENV{PROGRAMFILES}/flann" "$ENV{PROGRAMW6432}/flann" + PATH_SUFFIXES include) -find_path(FLANN_INCLUDE_DIR flann/flann.hpp - HINTS ${PC_FLANN_INCLUDEDIR} ${PC_FLANN_INCLUDE_DIRS} "${FLANN_ROOT}" "$ENV{FLANN_ROOT}" - PATHS "$ENV{PROGRAMFILES}/Flann" "$ENV{PROGRAMW6432}/Flann" - PATH_SUFFIXES include) + find_library(FLANN_LIBRARY + NAMES ${FLANN_RELEASE_NAME} + HINTS "${FLANN_ROOT}" "$ENV{FLANN_ROOT}" + PATHS "$ENV{PROGRAMFILES}/flann" "$ENV{PROGRAMW6432}/flann" + PATH_SUFFIXES lib) -find_library(FLANN_LIBRARY - NAMES ${FLANN_RELEASE_NAME} - HINTS ${PC_FLANN_LIBDIR} ${PC_FLANN_LIBRARY_DIRS} "${FLANN_ROOT}" "$ENV{FLANN_ROOT}" - PATHS "$ENV{PROGRAMFILES}/Flann" "$ENV{PROGRAMW6432}/Flann" - PATH_SUFFIXES lib) + find_library(FLANN_LIBRARY_DEBUG + NAMES ${FLANN_DEBUG_NAME} ${FLANN_RELEASE_NAME} + HINTS "${FLANN_ROOT}" "$ENV{FLANN_ROOT}" + PATHS "$ENV{PROGRAMFILES}/flann" "$ENV{PROGRAMW6432}/flann" + PATH_SUFFIXES lib debug/lib) -find_library(FLANN_LIBRARY_DEBUG - NAMES ${FLANN_DEBUG_NAME} ${FLANN_RELEASE_NAME} - HINTS ${PC_FLANN_LIBDIR} ${PC_FLANN_LIBRARY_DIRS} "${FLANN_ROOT}" "$ENV{FLANN_ROOT}" - PATHS "$ENV{PROGRAMFILES}/Flann" "$ENV{PROGRAMW6432}/Flann" - PATH_SUFFIXES lib) + if(NOT FLANN_LIBRARY_DEBUG) + set(FLANN_LIBRARY_DEBUG ${FLANN_LIBRARY}) + endif(NOT FLANN_LIBRARY_DEBUG) -if(NOT FLANN_LIBRARY_DEBUG) - set(FLANN_LIBRARY_DEBUG ${FLANN_LIBRARY}) -endif(NOT FLANN_LIBRARY_DEBUG) + set(FLANN_INCLUDE_DIRS ${FLANN_INCLUDE_DIR}) + set(FLANN_LIBRARIES optimized ${FLANN_LIBRARY} debug ${FLANN_LIBRARY_DEBUG}) -set(FLANN_INCLUDE_DIRS ${FLANN_INCLUDE_DIR}) -set(FLANN_LIBRARIES optimized ${FLANN_LIBRARY} debug ${FLANN_LIBRARY_DEBUG}) + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(FLANN DEFAULT_MSG FLANN_LIBRARY FLANN_INCLUDE_DIR) -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(FLANN DEFAULT_MSG FLANN_LIBRARY FLANN_INCLUDE_DIR) - -mark_as_advanced(FLANN_LIBRARY FLANN_LIBRARY_DEBUG FLANN_INCLUDE_DIR) + mark_as_advanced(FLANN_LIBRARY FLANN_LIBRARY_DEBUG FLANN_INCLUDE_DIR) +endif() if(FLANN_FOUND) message(STATUS "FLANN found (include: ${FLANN_INCLUDE_DIRS}, lib: ${FLANN_LIBRARIES})") diff --git a/cmake/Modules/FindOpenNI.cmake b/cmake/Modules/FindOpenNI.cmake index 9f772a4a..2c7b26ee 100644 --- a/cmake/Modules/FindOpenNI.cmake +++ b/cmake/Modules/FindOpenNI.cmake @@ -1,13 +1,14 @@ ############################################################################### # Find OpenNI # -# This sets the following variables: -# OPENNI_FOUND - True if OPENNI was found. -# OPENNI_INCLUDE_DIRS - Directories containing the OPENNI include files. -# OPENNI_LIBRARIES - Libraries needed to use OPENNI. -# OPENNI_DEFINITIONS - Compiler flags for OPENNI. +# find_package(OpenNI) # -# For libusb-1.0, add USB_10_ROOT if not found +# Variables defined by this module: +# +# OPENNI_FOUND True if OpenNI was found +# OPENNI_INCLUDE_DIRS The location(s) of OpenNI headers +# OPENNI_LIBRARIES Libraries needed to use OpenNI +# OPENNI_DEFINITIONS Compiler flags for OpenNI find_package(PkgConfig QUIET) @@ -47,31 +48,53 @@ if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8) set(OPENNI_SUFFIX 64) endif(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8) -#add a hint so that it can find it without the pkg-config +# Add a hint so that it can find it without the pkg-config find_path(OPENNI_INCLUDE_DIR XnStatus.h - HINTS ${PC_OPENNI_INCLUDEDIR} ${PC_OPENNI_INCLUDE_DIRS} /usr/include/openni /usr/include/ni /opt/local/include/ni "${OPENNI_ROOT}" "$ENV{OPENNI_ROOT}" + HINTS ${PC_OPENNI_INCLUDEDIR} + ${PC_OPENNI_INCLUDE_DIRS} + /usr/include/openni + /usr/include/ni + /opt/local/include/ni + "${OPENNI_ROOT}" + "$ENV{OPENNI_ROOT}" PATHS "$ENV{OPEN_NI_INSTALL_PATH${OPENNI_SUFFIX}}/Include" PATH_SUFFIXES openni include Include) -#add a hint so that it can find it without the pkg-config + +# Add a hint so that it can find it without the pkg-config find_library(OPENNI_LIBRARY NAMES OpenNI${OPENNI_SUFFIX} - HINTS ${PC_OPENNI_LIBDIR} ${PC_OPENNI_LIBRARY_DIRS} /usr/lib "${OPENNI_ROOT}" "$ENV{OPENNI_ROOT}" + HINTS ${PC_OPENNI_LIBDIR} + ${PC_OPENNI_LIBRARY_DIRS} + /usr/lib + "${OPENNI_ROOT}" + "$ENV{OPENNI_ROOT}" PATHS "$ENV{OPEN_NI_LIB${OPENNI_SUFFIX}}" PATH_SUFFIXES lib Lib Lib64) -if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") - set(OPENNI_LIBRARIES ${OPENNI_LIBRARY} ${LIBUSB_1_LIBRARIES}) -else() - set(OPENNI_LIBRARIES ${OPENNI_LIBRARY}) +if(OPENNI_INCLUDE_DIR AND OPENNI_LIBRARY) + + # Include directories + set(OPENNI_INCLUDE_DIRS ${OPENNI_INCLUDE_DIR}) + unset(OPENNI_INCLUDE_DIR) + mark_as_advanced(OPENNI_INCLUDE_DIRS) + + # Libraries + if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(OPENNI_LIBRARIES ${OPENNI_LIBRARY} ${LIBUSB_1_LIBRARIES}) + else() + set(OPENNI_LIBRARIES ${OPENNI_LIBRARY}) + endif() + unset(OPENNI_LIBRARY) + mark_as_advanced(OPENNI_LIBRARIES) + endif() include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(OpenNI DEFAULT_MSG OPENNI_LIBRARY OPENNI_INCLUDE_DIR) - -mark_as_advanced(OPENNI_LIBRARY OPENNI_INCLUDE_DIR) +find_package_handle_standard_args(OpenNI + FOUND_VAR OPENNI_FOUND + REQUIRED_VARS OPENNI_LIBRARIES OPENNI_INCLUDE_DIRS +) if(OPENNI_FOUND) - # Add the include directories - set(OPENNI_INCLUDE_DIRS ${OPENNI_INCLUDE_DIR}) - message(STATUS "OpenNI found (include: ${OPENNI_INCLUDE_DIRS}, lib: ${OPENNI_LIBRARY})") -endif(OPENNI_FOUND) + message(STATUS "OpenNI found (include: ${OPENNI_INCLUDE_DIRS}, lib: ${OPENNI_LIBRARIES})") +endif() diff --git a/cmake/Modules/FindOpenNI2.cmake b/cmake/Modules/FindOpenNI2.cmake index 713099a1..037a9a39 100644 --- a/cmake/Modules/FindOpenNI2.cmake +++ b/cmake/Modules/FindOpenNI2.cmake @@ -1,13 +1,14 @@ ############################################################################### -# Find OpenNI 2 +# Find OpenNI2 # -# This sets the following variables: -# OPENNI2_FOUND - True if OPENNI 2 was found. -# OPENNI2_INCLUDE_DIRS - Directories containing the OPENNI 2 include files. -# OPENNI2_LIBRARIES - Libraries needed to use OPENNI 2. -# OPENNI2_DEFINITIONS - Compiler flags for OPENNI 2. +# find_package(OpenNI2) # -# For libusb-1.0, add USB_10_ROOT if not found +# Variables defined by this module: +# +# OPENNI2_FOUND True if OpenNI2 was found +# OPENNI2_INCLUDE_DIRS The location(s) of OpenNI2 headers +# OPENNI2_LIBRARIES Libraries needed to use OpenNI2 +# OPENNI2_DEFINITIONS Compiler flags for OpenNI2 find_package(PkgConfig QUIET) @@ -47,34 +48,46 @@ if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8) set(OPENNI2_SUFFIX 64) endif(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8) -find_path(OPENNI2_INCLUDE_DIRS OpenNI.h - PATHS - "$ENV{OPENNI2_INCLUDE${OPENNI2_SUFFIX}}" # Win64 needs '64' suffix - /usr/include/openni2 # common path for deb packages +find_path(OPENNI2_INCLUDE_DIR OpenNI.h + PATHS "$ENV{OPENNI2_INCLUDE${OPENNI2_SUFFIX}}" # Win64 needs '64' suffix + "/usr/include/openni2" # common path for deb packages + PATH_SUFFIXES include/openni2 ) find_library(OPENNI2_LIBRARY - NAMES OpenNI2 # No suffix needed on Win64 - libOpenNI2 # Linux - PATHS "$ENV{OPENNI2_LIB${OPENNI2_SUFFIX}}" # Windows default path, Win64 needs '64' suffix - "$ENV{OPENNI2_REDIST}" # Linux install does not use a separate 'lib' directory - ) - -if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") - set(OPENNI2_LIBRARIES ${OPENNI2_LIBRARY} ${LIBUSB_1_LIBRARIES}) -else() - set(OPENNI2_LIBRARIES ${OPENNI2_LIBRARY}) -endif() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(OpenNI2 DEFAULT_MSG OPENNI2_LIBRARY OPENNI2_INCLUDE_DIRS) + NAMES OpenNI2 # No suffix needed on Win64 + libOpenNI2 # Linux + PATHS "$ENV{OPENNI2_LIB${OPENNI2_SUFFIX}}" # Windows default path, Win64 needs '64' suffix + "$ENV{OPENNI2_REDIST}" # Linux install does not use a separate 'lib' directory +) -mark_as_advanced(OPENNI2_LIBRARY OPENNI2_INCLUDE_DIRS) +if(OPENNI2_INCLUDE_DIR AND OPENNI2_LIBRARY) -if(OPENNI2_FOUND) - # Add the include directories + # Include directories set(OPENNI2_INCLUDE_DIRS ${OPENNI2_INCLUDE_DIR}) + unset(OPENNI2_INCLUDE_DIR) + mark_as_advanced(OPENNI2_INCLUDE_DIRS) + + # Libraries + if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(OPENNI2_LIBRARIES ${OPENNI2_LIBRARY} ${LIBUSB_1_LIBRARIES}) + else() + set(OPENNI2_LIBRARIES ${OPENNI2_LIBRARY}) + endif() + unset(OPENNI2_LIBRARY) + mark_as_advanced(OPENNI2_LIBRARIES) + set(OPENNI2_REDIST_DIR $ENV{OPENNI2_REDIST${OPENNI2_SUFFIX}}) - message(STATUS "OpenNI 2 found (include: ${OPENNI2_INCLUDE_DIRS}, lib: ${OPENNI2_LIBRARY}, redist: ${OPENNI2_REDIST_DIR})") -endif(OPENNI2_FOUND) + mark_as_advanced(OPENNI2_REDIST_DIR) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(OpenNI2 + FOUND_VAR OPENNI2_FOUND + REQUIRED_VARS OPENNI2_LIBRARIES OPENNI2_INCLUDE_DIRS +) + +if(OPENNI2_FOUND) + message(STATUS "OpenNI2 found (include: ${OPENNI2_INCLUDE_DIRS}, lib: ${OPENNI2_LIBRARIES})") +endif() diff --git a/cmake/Modules/FindQhull.cmake b/cmake/Modules/FindQhull.cmake index 698bd151..b596576c 100644 --- a/cmake/Modules/FindQhull.cmake +++ b/cmake/Modules/FindQhull.cmake @@ -6,7 +6,7 @@ # QHULL_INCLUDE_DIRS - Directories containing the QHULL include files. # QHULL_LIBRARIES - Libraries needed to use QHULL. # QHULL_DEFINITIONS - Compiler flags for QHULL. -# If QHULL_USE_STATIC is specified then look for static libraries ONLY else +# If QHULL_USE_STATIC is specified then look for static libraries ONLY else # look for shared ones set(QHULL_MAJOR_VERSION 6) @@ -16,13 +16,13 @@ if(QHULL_USE_STATIC) set(QHULL_DEBUG_NAME qhullstatic_d) else(QHULL_USE_STATIC) set(QHULL_RELEASE_NAME qhull_p qhull${QHULL_MAJOR_VERSION} qhull) - set(QHULL_DEBUG_NAME qhull_pd qhull${QHULL_MAJOR_VERSION}_d qhull_d${QHULL_MAJOR_VERSION} qhull_d) + set(QHULL_DEBUG_NAME qhull_p_d qhull${QHULL_MAJOR_VERSION}_d qhull_d${QHULL_MAJOR_VERSION} qhull_d) endif(QHULL_USE_STATIC) find_file(QHULL_HEADER NAMES libqhull/libqhull.h qhull.h HINTS "${QHULL_ROOT}" "$ENV{QHULL_ROOT}" "${QHULL_INCLUDE_DIR}" - PATHS "$ENV{PROGRAMFILES}/QHull" "$ENV{PROGRAMW6432}/QHull" + PATHS "$ENV{PROGRAMFILES}/QHull" "$ENV{PROGRAMW6432}/QHull" PATH_SUFFIXES qhull src/libqhull libqhull include) set(QHULL_HEADER "${QHULL_HEADER}" CACHE INTERNAL "QHull header" FORCE ) @@ -41,21 +41,19 @@ else(QHULL_HEADER) set(QHULL_INCLUDE_DIR "QHULL_INCLUDE_DIR-NOTFOUND") endif(QHULL_HEADER) -set(QHULL_INCLUDE_DIR "${QHULL_INCLUDE_DIR}" CACHE PATH "QHull include dir." FORCE) - -find_library(QHULL_LIBRARY +find_library(QHULL_LIBRARY NAMES ${QHULL_RELEASE_NAME} HINTS "${QHULL_ROOT}" "$ENV{QHULL_ROOT}" - PATHS "$ENV{PROGRAMFILES}/QHull" "$ENV{PROGRAMW6432}/QHull" + PATHS "$ENV{PROGRAMFILES}/QHull" "$ENV{PROGRAMW6432}/QHull" PATH_SUFFIXES project build bin lib) get_filename_component(QHULL_LIBRARY_NAME "${QHULL_LIBRARY}" NAME) -find_library(QHULL_LIBRARY_DEBUG +find_library(QHULL_LIBRARY_DEBUG NAMES ${QHULL_DEBUG_NAME} ${QHULL_RELEASE_NAME} HINTS "${QHULL_ROOT}" "$ENV{QHULL_ROOT}" - PATHS "$ENV{PROGRAMFILES}/QHull" "$ENV{PROGRAMW6432}/QHull" - PATH_SUFFIXES project build bin lib) + PATHS "$ENV{PROGRAMFILES}/QHull" "$ENV{PROGRAMW6432}/QHull" + PATH_SUFFIXES project build bin lib debug/lib) if(NOT QHULL_LIBRARY_DEBUG) set(QHULL_LIBRARY_DEBUG ${QHULL_LIBRARY}) @@ -63,13 +61,26 @@ endif(NOT QHULL_LIBRARY_DEBUG) get_filename_component(QHULL_LIBRARY_DEBUG_NAME "${QHULL_LIBRARY_DEBUG}" NAME) -set(QHULL_INCLUDE_DIRS ${QHULL_INCLUDE_DIR}) -set(QHULL_LIBRARIES optimized ${QHULL_LIBRARY} debug ${QHULL_LIBRARY_DEBUG}) +if(QHULL_INCLUDE_DIR AND QHULL_LIBRARY) -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Qhull DEFAULT_MSG QHULL_LIBRARY QHULL_INCLUDE_DIR) + # Include directories + set(QHULL_INCLUDE_DIRS ${QHULL_INCLUDE_DIR}) + unset(QHULL_INCLUDE_DIR) + mark_as_advanced(QHULL_INCLUDE_DIRS) -mark_as_advanced(QHULL_LIBRARY QHULL_LIBRARY_DEBUG QHULL_INCLUDE_DIR) + # Libraries + set(QHULL_LIBRARIES optimized ${QHULL_LIBRARY} debug ${QHULL_LIBRARY_DEBUG}) + unset(QHULL_LIBRARY) + unset(QHULL_LIBRARY_DEBUG) + mark_as_advanced(QHULL_LIBRARIES) + +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Qhull + FOUND_VAR QHULL_FOUND + REQUIRED_VARS QHULL_LIBRARIES QHULL_INCLUDE_DIRS +) if(QHULL_FOUND) set(HAVE_QHULL ON) diff --git a/cmake/Modules/NSIS.template.in b/cmake/Modules/NSIS.template.in index 9b3cefc7..fd17d643 100644 --- a/cmake/Modules/NSIS.template.in +++ b/cmake/Modules/NSIS.template.in @@ -861,7 +861,7 @@ Section "Uninstall" @CPACK_NSIS_DELETE_ICONS@ @CPACK_NSIS_DELETE_ICONS_EXTRA@ - ;Delete empty start menu parent diretories + ;Delete empty start menu parent directories StrCpy $MUI_TEMP "$SMPROGRAMS\$MUI_TEMP" startMenuDeleteLoop: @@ -880,7 +880,7 @@ Section "Uninstall" Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk" @CPACK_NSIS_DELETE_ICONS_EXTRA@ - ;Delete empty start menu parent diretories + ;Delete empty start menu parent directories StrCpy $MUI_TEMP "$SMPROGRAMS\$MUI_TEMP" secondStartMenuDeleteLoop: diff --git a/cmake/pcl_cpack.cmake b/cmake/pcl_cpack.cmake index ec785c5f..8869279f 100644 --- a/cmake/pcl_cpack.cmake +++ b/cmake/pcl_cpack.cmake @@ -45,7 +45,7 @@ if(WIN32) set(CPACK_NSIS_PACKAGE_NAME "${CPACK_NSIS_PACKAGE_NAME}-msvc2013-${win_system_name}") elseif(MSVC_VERSION EQUAL 1900) set(CPACK_NSIS_PACKAGE_NAME "${CPACK_NSIS_PACKAGE_NAME}-msvc2015-${win_system_name}") - elseif(MSVC_VERSION EQUAL 1910) + elseif(MSVC_VERSION MATCHES "^191[0-9]$") set(CPACK_NSIS_PACKAGE_NAME "${CPACK_NSIS_PACKAGE_NAME}-msvc2017-${win_system_name}") else() set(CPACK_NSIS_PACKAGE_NAME "${CPACK_NSIS_PACKAGE_NAME}-${win_system_name}") @@ -78,7 +78,7 @@ set(PCL_CPACK_CFG_FILE "${PCL_BINARY_DIR}/cpack_options.cmake") # Make the CPack input file. macro(PCL_MAKE_CPACK_INPUT) set(_cpack_cfg_in "${PCL_SOURCE_DIR}/cmake/cpack_options.cmake.in") - set(${_var} "${${_var}}\nset(CPACK_COMPONENT_GROUP_PCL_DESCRIPTION \"PCL headers and librairies\")\n") + set(${_var} "${${_var}}\nset(CPACK_COMPONENT_GROUP_PCL_DESCRIPTION \"PCL headers and libraries\")\n") # Prepare the components list set(PCL_CPACK_COMPONENTS) diff --git a/cmake/pcl_find_boost.cmake b/cmake/pcl_find_boost.cmake index 68920ccd..6489f395 100644 --- a/cmake/pcl_find_boost.cmake +++ b/cmake/pcl_find_boost.cmake @@ -19,6 +19,7 @@ if(${CMAKE_VERSION} VERSION_LESS 2.8.5) "1.46.0" "1.46" "1.45.0" "1.45" "1.44.0" "1.44" "1.43.0" "1.43") else(${CMAKE_VERSION} VERSION_LESS 2.8.5) set(Boost_ADDITIONAL_VERSIONS + "1.68.0" "1.68" "1.67.0" "1.67" "1.66.0" "1.66" "1.65.1" "1.65.0" "1.65" "1.64.0" "1.64" "1.63.0" "1.63" "1.62.0" "1.62" "1.61.0" "1.61" "1.60.0" "1.60" "1.59.0" "1.59" "1.58.0" "1.58" "1.57.0" "1.57" "1.56.0" "1.56" "1.55.0" "1.55" "1.54.0" "1.54" "1.53.0" "1.53" "1.52.0" "1.52" "1.51.0" "1.51" @@ -36,10 +37,10 @@ endif(Boost_SERIALIZATION_FOUND) # Required boost modules if(WITH_OPENNI2) -set(BOOST_REQUIRED_MODULES system filesystem thread date_time iostreams chrono) +set(BOOST_REQUIRED_MODULES filesystem thread date_time iostreams chrono system) find_package(Boost 1.47.0 REQUIRED COMPONENTS ${BOOST_REQUIRED_MODULES}) else() -set(BOOST_REQUIRED_MODULES system filesystem thread date_time iostreams) +set(BOOST_REQUIRED_MODULES filesystem thread date_time iostreams system) find_package(Boost 1.40.0 REQUIRED COMPONENTS ${BOOST_REQUIRED_MODULES}) endif() diff --git a/cmake/pcl_find_cuda.cmake b/cmake/pcl_find_cuda.cmake index fd9cfa90..ee70fdb4 100644 --- a/cmake/pcl_find_cuda.cmake +++ b/cmake/pcl_find_cuda.cmake @@ -1,70 +1,75 @@ # Find CUDA - - if(MSVC) - # Setting this to true brakes Visual Studio builds. - set(CUDA_ATTACH_VS_BUILD_RULE_TO_CUDA_FILE OFF CACHE BOOL "CUDA_ATTACH_VS_BUILD_RULE_TO_CUDA_FILE") + # Setting this to true brakes Visual Studio builds. + set(CUDA_ATTACH_VS_BUILD_RULE_TO_CUDA_FILE OFF CACHE BOOL "CUDA_ATTACH_VS_BUILD_RULE_TO_CUDA_FILE") endif() set(CUDA_FIND_QUIETLY TRUE) -find_package(CUDA 4) +find_package(CUDA) if(CUDA_FOUND) - message(STATUS "Found CUDA Toolkit v${CUDA_VERSION_STRING}") - - if(${CUDA_VERSION_STRING} VERSION_LESS "7.5") - # Recent versions of cmake set CUDA_HOST_COMPILER to CMAKE_C_COMPILER which - # on OSX defaults to clang (/usr/bin/cc), but this is not a supported cuda - # compiler. So, here we will preemptively set CUDA_HOST_COMPILER to gcc if - # that compiler exists in /usr/bin. This will not override an existing cache - # value if the user has passed CUDA_HOST_COMPILER on the command line. - if (NOT DEFINED CUDA_HOST_COMPILER AND CMAKE_C_COMPILER_ID STREQUAL "Clang" AND EXISTS /usr/bin/gcc) - set(CUDA_HOST_COMPILER /usr/bin/gcc CACHE FILEPATH "Host side compiler used by NVCC") - message(STATUS "Setting CMAKE_HOST_COMPILER to /usr/bin/gcc instead of ${CMAKE_C_COMPILER}. See http://dev.pointclouds.org/issues/979") - endif() + message(STATUS "Found CUDA Toolkit v${CUDA_VERSION_STRING}") + set(HAVE_CUDA TRUE) + + if(${CUDA_VERSION_STRING} VERSION_LESS "7.5") + # Recent versions of cmake set CUDA_HOST_COMPILER to CMAKE_C_COMPILER which + # on OSX defaults to clang (/usr/bin/cc), but this is not a supported cuda + # compiler. So, here we will preemptively set CUDA_HOST_COMPILER to gcc if + # that compiler exists in /usr/bin. This will not override an existing cache + # value if the user has passed CUDA_HOST_COMPILER on the command line. + if (NOT DEFINED CUDA_HOST_COMPILER AND CMAKE_C_COMPILER_ID STREQUAL "Clang" AND EXISTS /usr/bin/gcc) + set(CUDA_HOST_COMPILER /usr/bin/gcc CACHE FILEPATH "Host side compiler used by NVCC") + message(STATUS "Setting CMAKE_HOST_COMPILER to /usr/bin/gcc instead of ${CMAKE_C_COMPILER}. See http://dev.pointclouds.org/issues/979") + endif() + + # Send a warning if CUDA_HOST_COMPILER is set to a compiler that is known + # to be unsupported. + if (CUDA_HOST_COMPILER STREQUAL CMAKE_C_COMPILER AND CMAKE_C_COMPILER_ID STREQUAL "Clang") + message(WARNING "CUDA_HOST_COMPILER is set to an unsupported compiler: ${CMAKE_C_COMPILER}. See http://dev.pointclouds.org/issues/979") + endif() + endif() - # Send a warning if CUDA_HOST_COMPILER is set to a compiler that is known - # to be unsupported. - if (CUDA_HOST_COMPILER STREQUAL CMAKE_C_COMPILER AND CMAKE_C_COMPILER_ID STREQUAL "Clang") - message(WARNING "CUDA_HOST_COMPILER is set to an unsupported compiler: ${CMAKE_C_COMPILER}. See http://dev.pointclouds.org/issues/979") - endif() - endif() + # CUDA_ARCH_BIN is a space separated list of versions to include in output so-file. So you can set CUDA_ARCH_BIN = 10 11 12 13 20 + # Also user can specify virtual arch in parenthesis to limit instructions set, + # for example CUDA_ARCH_BIN = 11(11) 12(11) 13(11) 20(11) 21(11) -> forces using only sm_11 instructions. + # The CMake scripts interpret XX as XX (XX). This allows user to omit parenthesis. + # Arch 21 is an exceptional case since it doesn't have own sm_21 instructions set. + # So 21 = 21(21) is an invalid configuration and user has to explicitly force previous sm_20 instruction set via 21(20). + # CUDA_ARCH_BIN adds support of only listed GPUs. As alternative CMake scripts also parse 'CUDA_ARCH_PTX' variable, + # which is a list of intermediate PTX codes to include in final so-file. The PTX code can/will be JIT compiled for any current or future GPU. + # To add support of older GPU for kinfu, I would embed PTX 11 and 12 into so-file. GPU with sm_13 will run PTX 12 code (no difference for kinfu) - # CUDA_ARCH_BIN is a space separated list of versions to include in output so-file. So you can set CUDA_ARCH_BIN = 10 11 12 13 20 - # Also user can specify virtual arch in parenthesis to limit instructions set, - # for example CUDA_ARCH_BIN = 11(11) 12(11) 13(11) 20(11) 21(11) -> forces using only sm_11 instructions. - # The CMake scripts interpret XX as XX (XX). This allows user to omit parenthesis. - # Arch 21 is an exceptional case since it doesn't have own sm_21 instructions set. - # So 21 = 21(21) is an invalid configuration and user has to explicitly force previous sm_20 instruction set via 21(20). - # CUDA_ARCH_BIN adds support of only listed GPUs. As alternative CMake scripts also parse 'CUDA_ARCH_PTX' variable, - # which is a list of intermediate PTX codes to include in final so-file. The PTX code can/will be JIT compiled for any current or future GPU. - # To add support of older GPU for kinfu, I would embed PTX 11 and 12 into so-file. GPU with sm_13 will run PTX 12 code (no difference for kinfu) - - # Find a complete list for CUDA compute capabilities at http://developer.nvidia.com/cuda-gpus + # Find a complete list for CUDA compute capabilities at http://developer.nvidia.com/cuda-gpus - if(NOT ${CUDA_VERSION_STRING} VERSION_LESS "8.0") - set(__cuda_arch_bin "2.0 2.1(2.0) 3.0 3.5 5.0 5.2 5.3 6.0 6.1") - elseif(NOT ${CUDA_VERSION_STRING} VERSION_LESS "6.5") - set(__cuda_arch_bin "2.0 2.1(2.0) 3.0 3.5 5.0 5.2") - elseif(NOT ${CUDA_VERSION_STRING} VERSION_LESS "6.0") - set(__cuda_arch_bin "2.0 2.1(2.0) 3.0 3.5 5.0") - elseif(NOT ${CUDA_VERSION_STRING} VERSION_LESS "5.0") - set(__cuda_arch_bin "2.0 2.1(2.0) 3.0 3.5") - elseif(${CUDA_VERSION_STRING} VERSION_GREATER "4.1") - set(__cuda_arch_bin "2.0 2.1(2.0) 3.0") - else() - set(__cuda_arch_bin "2.0 2.1(2.0)") - endif() + if(NOT ${CUDA_VERSION_STRING} VERSION_LESS "10.0") + set(__cuda_arch_bin "3.0 3.5 5.0 5.2 5.3 6.0 6.1 7.0 7.2 7.5") + elseif(NOT ${CUDA_VERSION_STRING} VERSION_LESS "9.1") + set(__cuda_arch_bin "3.0 3.5 5.0 5.2 5.3 6.0 6.1 7.0 7.2") + elseif(NOT ${CUDA_VERSION_STRING} VERSION_LESS "9.0") + set(__cuda_arch_bin "3.0 3.5 5.0 5.2 5.3 6.0 6.1 7.0") + elseif(NOT ${CUDA_VERSION_STRING} VERSION_LESS "8.0") + set(__cuda_arch_bin "2.0 2.1(2.0) 3.0 3.5 5.0 5.2 5.3 6.0 6.1") + elseif(NOT ${CUDA_VERSION_STRING} VERSION_LESS "6.5") + set(__cuda_arch_bin "2.0 2.1(2.0) 3.0 3.5 5.0 5.2") + elseif(NOT ${CUDA_VERSION_STRING} VERSION_LESS "6.0") + set(__cuda_arch_bin "2.0 2.1(2.0) 3.0 3.5 5.0") + elseif(NOT ${CUDA_VERSION_STRING} VERSION_LESS "5.0") + set(__cuda_arch_bin "2.0 2.1(2.0) 3.0 3.5") + elseif(${CUDA_VERSION_STRING} VERSION_GREATER "4.1") + set(__cuda_arch_bin "2.0 2.1(2.0) 3.0") + else() + set(__cuda_arch_bin "2.0 2.1(2.0)") + endif() - set(CUDA_ARCH_BIN ${__cuda_arch_bin} CACHE STRING "Specify 'real' GPU architectures to build binaries for, BIN(PTX) format is supported") + set(CUDA_ARCH_BIN ${__cuda_arch_bin} CACHE STRING "Specify 'real' GPU architectures to build binaries for, BIN(PTX) format is supported") - set(CUDA_ARCH_PTX "" CACHE STRING "Specify 'virtual' PTX arch to build PTX intermediate code for. Example: 1.0 1.2 or 10 12") - #set(CUDA_ARCH_PTX "1.1 1.2" CACHE STRING "Specify 'virtual' PTX arch to build PTX intermediate code for. Example: 1.0 1.2 or 10 12") + set(CUDA_ARCH_PTX "" CACHE STRING "Specify 'virtual' PTX arch to build PTX intermediate code for. Example: 1.0 1.2 or 10 12") + #set(CUDA_ARCH_PTX "1.1 1.2" CACHE STRING "Specify 'virtual' PTX arch to build PTX intermediate code for. Example: 1.0 1.2 or 10 12") - # Guess this macros will be included in cmake distributive - include(${PCL_SOURCE_DIR}/cmake/CudaComputeTargetFlags.cmake) - APPEND_TARGET_ARCH_FLAGS() + # Guess this macros will be included in cmake distributive + include(${PCL_SOURCE_DIR}/cmake/CudaComputeTargetFlags.cmake) + APPEND_TARGET_ARCH_FLAGS() # Prevent compilation issues between recent gcc versions and old CUDA versions list(APPEND CUDA_NVCC_FLAGS "-D_FORCE_INLINES") diff --git a/cmake/pcl_find_sse.cmake b/cmake/pcl_find_sse.cmake index a1ebfe6b..3b6a3e5f 100644 --- a/cmake/pcl_find_sse.cmake +++ b/cmake/pcl_find_sse.cmake @@ -4,35 +4,31 @@ macro(PCL_CHECK_FOR_SSE) set(SSE_FLAGS) set(SSE_DEFINITIONS) - # Test CLANG - #if(CMAKE_COMPILER_IS_CLANG) - # if(APPLE) - # SET(SSE_FLAGS "${SSE_FLAGS} -march=native") - # endif(APPLE) - #endif(CMAKE_COMPILER_IS_CLANG) - - # Test GCC/G++ - if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) - execute_process(COMMAND ${CMAKE_CXX_COMPILER} "-dumpversion" - OUTPUT_VARIABLE GCC_VERSION_STRING) - if(GCC_VERSION_STRING VERSION_GREATER 4.2 AND NOT APPLE AND NOT CMAKE_CROSSCOMPILING) - SET(SSE_FLAGS "${SSE_FLAGS} -march=native") + if(NOT CMAKE_CROSSCOMPILING) + # Test GCC/G++ and CLANG + if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANG) + list(APPEND SSE_FLAGS "-march=native") message(STATUS "Using CPU native flags for SSE optimization: ${SSE_FLAGS}") endif() endif() - # Unfortunately we need to check for SSE to enable "-mfpmath=sse" alongside + # Unfortunately we need to check for SSE to enable "-mfpmath=sse" alongside # "-march=native". The reason for this is that by default, 32bit architectures # tend to use the x87 FPU (which has 80 bit internal precision), thus leading # to different results than 64bit architectures which are using SSE2 (64 bit internal - # precision). One solution would be to use "-ffloat-store" on 32bit (see + # precision). One solution would be to use "-ffloat-store" on 32bit (see # http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html), but that slows code down, # so the preferred solution is to try "-mpfmath=sse" first. include(CheckCXXSourceRuns) set(CMAKE_REQUIRED_FLAGS) check_cxx_source_runs(" - #include + // Intel compiler defines an incompatible _mm_malloc signature + #if defined(__INTEL_COMPILER) + #include + #else + #include + #endif int main() { void* mem = _mm_malloc (100, 16); @@ -131,7 +127,7 @@ macro(PCL_CHECK_FOR_SSE) elseif(MSVC AND NOT CMAKE_CL_64) set(CMAKE_REQUIRED_FLAGS "/arch:SSE2") endif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANG) - + check_cxx_source_runs(" #include int main () @@ -169,27 +165,27 @@ macro(PCL_CHECK_FOR_SSE) if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANG) if(HAVE_SSE4_2_EXTENSIONS) - SET(SSE_FLAGS "${SSE_FLAGS} -msse4.2 -mfpmath=sse") + list(APPEND SSE_FLAGS "-msse4.2" "-mfpmath=sse") elseif(HAVE_SSE4_1_EXTENSIONS) - SET(SSE_FLAGS "${SSE_FLAGS} -msse4.1 -mfpmath=sse") + list(APPEND SSE_FLAGS "-msse4.1" "-mfpmath=sse") elseif(HAVE_SSSE3_EXTENSIONS) - SET(SSE_FLAGS "${SSE_FLAGS} -mssse3 -mfpmath=sse") + list(APPEND SSE_FLAGS "-mssse3" "-mfpmath=sse") elseif(HAVE_SSE3_EXTENSIONS) - SET(SSE_FLAGS "${SSE_FLAGS} -msse3 -mfpmath=sse") + list(APPEND SSE_FLAGS "-msse3" "-mfpmath=sse") elseif(HAVE_SSE2_EXTENSIONS) - SET(SSE_FLAGS "${SSE_FLAGS} -msse2 -mfpmath=sse") + list(APPEND SSE_FLAGS "-msse2" "-mfpmath=sse") elseif(HAVE_SSE_EXTENSIONS) - SET(SSE_FLAGS "${SSE_FLAGS} -msse -mfpmath=sse") + list(APPEND SSE_FLAGS "-msse" "-mfpmath=sse") else() # Setting -ffloat-store to alleviate 32bit vs 64bit discrepancies on non-SSE # platforms. - set(SSE_FLAGS "-ffloat-store") + list(APPEND SSE_FLAGS "-ffloat-store") endif() elseif(MSVC AND NOT CMAKE_CL_64) if(HAVE_SSE2_EXTENSIONS) - SET(SSE_FLAGS "${SSE_FLAGS} /arch:SSE2") + list(APPEND SSE_FLAGS "/arch:SSE2") elseif(HAVE_SSE_EXTENSIONS) - SET(SSE_FLAGS "${SSE_FLAGS} /arch:SSE") + list(APPEND SSE_FLAGS "/arch:SSE") endif(HAVE_SSE2_EXTENSIONS) endif() @@ -204,4 +200,5 @@ macro(PCL_CHECK_FOR_SSE) SET(SSE_DEFINITIONS "${SSE_DEFINITIONS} -D__SSE__") endif() endif() + string(REPLACE ";" " " SSE_FLAGS_STR "${SSE_FLAGS}") endmacro(PCL_CHECK_FOR_SSE) diff --git a/cmake/pcl_options.cmake b/cmake/pcl_options.cmake index 242c19b0..687663b9 100644 --- a/cmake/pcl_options.cmake +++ b/cmake/pcl_options.cmake @@ -26,6 +26,10 @@ mark_as_advanced(PCL_BUILD_WITH_BOOST_DYNAMIC_LINKING_WIN32) option(PCL_BUILD_WITH_FLANN_DYNAMIC_LINKING_WIN32 "Build against a dynamically linked FLANN on Win32 platforms." OFF) mark_as_advanced(PCL_BUILD_WITH_FLANN_DYNAMIC_LINKING_WIN32) +# Build with dynamic linking for QHull (advanced users) +option(PCL_BUILD_WITH_QHULL_DYNAMIC_LINKING_WIN32 "Build against a dynamically linked QHull on Win32 platforms." OFF) +mark_as_advanced(PCL_BUILD_WITH_QHULL_DYNAMIC_LINKING_WIN32) + # Precompile for a minimal set of point types instead of all. option(PCL_ONLY_CORE_POINT_TYPES "Compile explicitly only for a small subset of point types (e.g., pcl::PointXYZ instead of PCL_XYZ_POINT_TYPES)." OFF) mark_as_advanced(PCL_ONLY_CORE_POINT_TYPES) diff --git a/cmake/pcl_pclconfig.cmake b/cmake/pcl_pclconfig.cmake index 75c75d58..feb87aba 100644 --- a/cmake/pcl_pclconfig.cmake +++ b/cmake/pcl_pclconfig.cmake @@ -2,15 +2,25 @@ set(PCL_SUBSYSTEMS_MODULES ${PCL_SUBSYSTEMS}) list(REMOVE_ITEM PCL_SUBSYSTEMS_MODULES tools cuda_apps global_tests proctor examples) + +file(GLOB PCLCONFIG_FIND_MODULES "${PCL_SOURCE_DIR}/cmake/Modules/*.cmake") +install(FILES ${PCLCONFIG_FIND_MODULES} COMPONENT pclconfig DESTINATION ${PCLCONFIG_INSTALL_DIR}/Modules) + set(PCLCONFIG_AVAILABLE_COMPONENTS) set(PCLCONFIG_AVAILABLE_COMPONENTS_LIST) set(PCLCONFIG_INTERNAL_DEPENDENCIES) set(PCLCONFIG_EXTERNAL_DEPENDENCIES) set(PCLCONFIG_OPTIONAL_DEPENDENCIES) -set(PCLCONFIG_SSE_DEFINITIONS "${SSE_FLAGS} ${SSE_DEFINITIONS}") +set(PCLCONFIG_SSE_DEFINITIONS "${SSE_DEFINITIONS}") +set(PCLCONFIG_SSE_COMPILE_OPTIONS ${SSE_FLAGS}) + foreach(_ss ${PCL_SUBSYSTEMS_MODULES}) PCL_GET_SUBSYS_STATUS(_status ${_ss}) - if(_status) + + # do not include test targets + string(REGEX MATCH "^tests_" _is_test ${_ss}) + + if(_status AND NOT _is_test) set(PCLCONFIG_AVAILABLE_COMPONENTS "${PCLCONFIG_AVAILABLE_COMPONENTS} ${_ss}") set(PCLCONFIG_AVAILABLE_COMPONENTS_LIST "${PCLCONFIG_AVAILABLE_COMPONENTS_LIST}\n# - ${_ss}") GET_IN_MAP(_deps PCL_SUBSYS_DEPS ${_ss}) @@ -33,7 +43,11 @@ foreach(_ss ${PCL_SUBSYSTEMS_MODULES}) if(_opt_deps) set(PCLCONFIG_OPTIONAL_DEPENDENCIES "${PCLCONFIG_OPTIONAL_DEPENDENCIES}set(pcl_${_ss}_opt_dep ") foreach(_opt_dep ${_opt_deps}) - set(PCLCONFIG_OPTIONAL_DEPENDENCIES "${PCLCONFIG_OPTIONAL_DEPENDENCIES}${_opt_dep} ") + string(TOUPPER "WITH_${_opt_dep}" _tmp) + string(REGEX REPLACE "-(.*)" "" _condition ${_tmp}) #libusb-1.0 case + if(${_condition}) + set(PCLCONFIG_OPTIONAL_DEPENDENCIES "${PCLCONFIG_OPTIONAL_DEPENDENCIES}${_opt_dep} ") + endif() endforeach(_opt_dep) set(PCLCONFIG_OPTIONAL_DEPENDENCIES "${PCLCONFIG_OPTIONAL_DEPENDENCIES})\n") endif(_opt_deps) @@ -58,7 +72,7 @@ foreach(_ss ${PCL_SUBSYSTEMS_MODULES}) endif (_sub_status) endforeach(_sub) endif (${PCL_SUBSYS_SUBSYS}) - endif(_status) + endif() endforeach(_ss) #Boost modules diff --git a/cmake/pcl_targets.cmake b/cmake/pcl_targets.cmake index 80dfedf2..22049d26 100644 --- a/cmake/pcl_targets.cmake +++ b/cmake/pcl_targets.cmake @@ -110,10 +110,10 @@ macro(PCL_SUBSYS_DEPEND _var _name) if(SUBSYS_EXT_DEPS) foreach(_dep ${SUBSYS_EXT_DEPS}) string(TOUPPER "${_dep}_found" EXT_DEP_FOUND) - if(NOT ${EXT_DEP_FOUND} OR (NOT (${EXT_DEP_FOUND} STREQUAL "TRUE"))) + if(NOT ${EXT_DEP_FOUND} AND (NOT (${EXT_DEP_FOUND} STREQUAL "TRUE"))) set(${_var} FALSE) PCL_SET_SUBSYS_STATUS(${_name} FALSE "Requires external library ${_dep}.") - endif(NOT ${EXT_DEP_FOUND} OR (NOT (${EXT_DEP_FOUND} STREQUAL "TRUE"))) + endif(NOT ${EXT_DEP_FOUND} AND (NOT (${EXT_DEP_FOUND} STREQUAL "TRUE"))) endforeach(_dep) endif(SUBSYS_EXT_DEPS) if(SUBSYS_OPT_DEPS) @@ -165,10 +165,10 @@ macro(PCL_SUBSUBSYS_DEPEND _var _parent _name) if(SUBSUBSYS_EXT_DEPS) foreach(_dep ${SUBSUBSYS_EXT_DEPS}) string(TOUPPER "${_dep}_found" EXT_DEP_FOUND) - if(NOT ${EXT_DEP_FOUND} OR (NOT ("${EXT_DEP_FOUND}" STREQUAL "TRUE"))) + if(NOT ${EXT_DEP_FOUND} AND (NOT ("${EXT_DEP_FOUND}" STREQUAL "TRUE"))) set(${_var} FALSE) PCL_SET_SUBSYS_STATUS(${_parent}_${_name} FALSE "Requires external library ${_dep}.") - endif(NOT ${EXT_DEP_FOUND} OR (NOT ("${EXT_DEP_FOUND}" STREQUAL "TRUE"))) + endif(NOT ${EXT_DEP_FOUND} AND (NOT ("${EXT_DEP_FOUND}" STREQUAL "TRUE"))) endforeach(_dep) endif(SUBSUBSYS_EXT_DEPS) endif(${_var} AND (NOT ("${subsys_status}" STREQUAL "AUTO_OFF"))) @@ -450,7 +450,7 @@ endmacro(PCL_ADD_LINKFLAGS) ############################################################################### # Make a pkg-config file for a library. Do not include general PCL stuff in the -# arguments; they will be added automaticaly. +# arguments; they will be added automatically. # _name The library name. "pcl_" will be preprended to this. # _component The part of PCL that this pkg-config file belongs to. # _desc Description of the library. @@ -487,7 +487,7 @@ endmacro(PCL_MAKE_PKGCONFIG) # Essentially a duplicate of PCL_MAKE_PKGCONFIG, but # ensures that no -L or l flags will be created # Do not include general PCL stuff in the -# arguments; they will be added automaticaly. +# arguments; they will be added automatically. # _name The library name. "pcl_" will be preprended to this. # _component The part of PCL that this pkg-config file belongs to. # _desc Description of the library. @@ -623,7 +623,7 @@ endmacro(PCL_GET_SUBSUBSYS_STATUS) ############################################################################### # Set the hyperstatus of a subsystem and its dependee # _name Subsystem name. -# _dependee Dependant subsystem. +# _dependee Dependent subsystem. # _status AUTO_OFF to disable AUTO_ON to enable # ARGN[0] Reason for not building. macro(PCL_SET_SUBSYS_HYPERSTATUS _name _dependee _status) @@ -636,7 +636,7 @@ endmacro(PCL_SET_SUBSYS_HYPERSTATUS) ############################################################################### # Get the hyperstatus of a subsystem and its dependee # _name IN subsystem name. -# _dependee IN dependant subsystem. +# _dependee IN dependent subsystem. # _var OUT hyperstatus # ARGN[0] Reason for not building. macro(PCL_GET_SUBSYS_HYPERSTATUS _var _name) @@ -852,7 +852,7 @@ endmacro(PCL_ADD_DOC) # This macro adds on option named "WITH_NAME", where NAME is the capitalized # dependency name. The user may use this option to control whether the # corresponding grabber should be built or not. Also an attempt to find a -# package with the given name is made. If it is not successfull, then the +# package with the given name is made. If it is not successful, then the # "WITH_NAME" option is coerced to FALSE. macro(PCL_ADD_GRABBER_DEPENDENCY _name _description) string(TOUPPER ${_name} _name_capitalized) diff --git a/cmake/pcl_utils.cmake b/cmake/pcl_utils.cmake index 69f1e76a..41a64a26 100644 --- a/cmake/pcl_utils.cmake +++ b/cmake/pcl_utils.cmake @@ -405,7 +405,7 @@ macro(sort_relative _list _sorted_list _to_sort_relative) if(NOT (list_length EQUAL to_sort_list_length)) message(FATAL_ERROR "size mismatch between ${_to_sort_relative} ${to_sort_list_length} and ${_list} ${list_length}") endif(NOT (list_length EQUAL to_sort_list_length)) - # unset the temporary list to avoid suprises (I had some them and were hard to find) + # unset the temporary list to avoid surprises (I had some them and were hard to find) unset(tmp_list) # fill it with a dummy value fill_list(tmp_list list_length "#") diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index ece6f0a3..150afb6b 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -146,11 +146,6 @@ if(build) include/pcl/impl/cloud_iterator.hpp ) - set(ros_incs - include/pcl/ros/conversions.h - include/pcl/ros/register_point_struct.h - ) - set(tools_incs include/pcl/console/parse.h include/pcl/console/print.h @@ -168,8 +163,7 @@ if(build) set(LIB_NAME "pcl_${SUBSYS_NAME}") include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include") - PCL_ADD_LIBRARY("${LIB_NAME}" "${SUBSYS_NAME}" ${srcs} ${kissfft_srcs} ${incs} ${common_incs} ${impl_incs} ${ros_incs} ${tools_incs} ${kissfft_incs} ${common_incs_impl} ${range_image_incs} ${range_image_incs_impl}) - #PCL_ADD_LIBRARY("${LIB_NAME}" "${SUBSYS_NAME}" ${srcs} ${incs} ${common_incs} ${impl_incs} ${ros_incs} ${tools_incs} ${common_incs_impl} ${range_image_incs} ${range_image_incs_impl}) + PCL_ADD_LIBRARY("${LIB_NAME}" "${SUBSYS_NAME}" ${srcs} ${kissfft_srcs} ${incs} ${common_incs} ${impl_incs} ${tools_incs} ${kissfft_incs} ${common_incs_impl} ${range_image_incs} ${range_image_incs_impl}) PCL_MAKE_PKGCONFIG("${LIB_NAME}" "${SUBSYS_NAME}" "${SUBSYS_DESC}" "" "" "" "" "") @@ -179,7 +173,6 @@ if(build) PCL_ADD_INCLUDES("${SUBSYS_NAME}" common/fft ${kissfft_incs}) PCL_ADD_INCLUDES("${SUBSYS_NAME}" common/impl ${common_incs_impl}) PCL_ADD_INCLUDES("${SUBSYS_NAME}" impl ${impl_incs}) - PCL_ADD_INCLUDES("${SUBSYS_NAME}" ros ${ros_incs}) PCL_ADD_INCLUDES("${SUBSYS_NAME}" console ${tools_incs}) PCL_ADD_INCLUDES("${SUBSYS_NAME}" range_image ${range_image_incs}) PCL_ADD_INCLUDES("${SUBSYS_NAME}" range_image/impl ${range_image_incs_impl}) diff --git a/common/include/pcl/common/bivariate_polynomial.h b/common/include/pcl/common/bivariate_polynomial.h index ccb313f9..357764e4 100644 --- a/common/include/pcl/common/bivariate_polynomial.h +++ b/common/include/pcl/common/bivariate_polynomial.h @@ -70,7 +70,7 @@ namespace pcl void setDegree (int new_degree); - /** How many parametes has a bivariate polynomial with this degree */ + /** How many parameters has a bivariate polynomial with this degree */ unsigned int getNoOfParameters () const { return getNoOfParametersFromDegree (degree);} @@ -108,7 +108,7 @@ namespace pcl void readBinary (const char* filename); - /** How many parametes has a bivariate polynomial of the given degree */ + /** How many parameters has a bivariate polynomial of the given degree */ static unsigned int getNoOfParametersFromDegree (int n) { return ((n+2)* (n+1))/2;} diff --git a/common/include/pcl/common/centroid.h b/common/include/pcl/common/centroid.h index 535ddf0c..7d8e1173 100644 --- a/common/include/pcl/common/centroid.h +++ b/common/include/pcl/common/centroid.h @@ -56,9 +56,9 @@ namespace pcl /** \brief Compute the 3D (X-Y-Z) centroid of a set of points and return it as a 3D vector. * \param[in] cloud_iterator an iterator over the input point cloud * \param[out] centroid the output centroid - * \return number of valid point used to determine the centroid. In case of dense point clouds, this is the same as the size of input cloud. + * \return number of valid points used to determine the centroid. In case of dense point clouds, this is the same as the size of input cloud. * \note if return value is 0, the centroid is not changed, thus not valid. - * The last compononent of the vector is set to 1, this allow to transform the centroid vector with 4x4 matrices. + * The last component of the vector is set to 1, this allows to transform the centroid vector with 4x4 matrices. * \ingroup common */ template inline unsigned int @@ -82,9 +82,9 @@ namespace pcl /** \brief Compute the 3D (X-Y-Z) centroid of a set of points and return it as a 3D vector. * \param[in] cloud the input point cloud * \param[out] centroid the output centroid - * \return number of valid point used to determine the centroid. In case of dense point clouds, this is the same as the size of input cloud. + * \return number of valid points used to determine the centroid. In case of dense point clouds, this is the same as the size of input cloud. * \note if return value is 0, the centroid is not changed, thus not valid. - * The last compononent of the vector is set to 1, this allow to transform the centroid vector with 4x4 matrices. + * The last component of the vector is set to 1, this allows to transform the centroid vector with 4x4 matrices. * \ingroup common */ template inline unsigned int @@ -110,9 +110,9 @@ namespace pcl * \param[in] cloud the input point cloud * \param[in] indices the point cloud indices that need to be used * \param[out] centroid the output centroid - * \return number of valid point used to determine the centroid. In case of dense point clouds, this is the same as the size of input cloud. + * \return number of valid points used to determine the centroid. In case of dense point clouds, this is the same as the size of input indices. * \note if return value is 0, the centroid is not changed, thus not valid. - * The last compononent of the vector is set to 1, this allow to transform the centroid vector with 4x4 matrices. + * The last component of the vector is set to 1, this allows to transform the centroid vector with 4x4 matrices. * \ingroup common */ template inline unsigned int @@ -141,9 +141,9 @@ namespace pcl * \param[in] cloud the input point cloud * \param[in] indices the point cloud indices that need to be used * \param[out] centroid the output centroid - * \return number of valid point used to determine the centroid. In case of dense point clouds, this is the same as the size of input cloud. + * \return number of valid points used to determine the centroid. In case of dense point clouds, this is the same as the size of input indices. * \note if return value is 0, the centroid is not changed, thus not valid. - * The last compononent of the vector is set to 1, this allow to transform the centroid vector with 4x4 matrices. + * The last component of the vector is set to 1, this allows to transform the centroid vector with 4x4 matrices. * \ingroup common */ template inline unsigned int @@ -171,11 +171,11 @@ namespace pcl * The result is returned as a Eigen::Matrix3f. * Note: the covariance matrix is not normalized with the number of * points. For a normalized covariance, please use - * computeNormalizedCovarianceMatrix. + * computeCovarianceMatrixNormalized. * \param[in] cloud the input point cloud * \param[in] centroid the centroid of the set of points in the cloud * \param[out] covariance_matrix the resultant 3x3 covariance matrix - * \return number of valid point used to determine the covariance matrix. + * \return number of valid points used to determine the covariance matrix. * In case of dense point clouds, this is the same as the size of input cloud. * \note if return value is 0, the covariance matrix is not changed, thus not valid. * \ingroup common @@ -204,13 +204,13 @@ namespace pcl /** \brief Compute normalized the 3x3 covariance matrix of a given set of points. * The result is returned as a Eigen::Matrix3f. * Normalized means that every entry has been divided by the number of points in the point cloud. - * For small number of points, or if you want explicitely the sample-variance, use computeCovarianceMatrix + * For small number of points, or if you want explicitly the sample-variance, use computeCovarianceMatrix * and scale the covariance matrix with 1 / (n-1), where n is the number of points used to calculate * the covariance matrix and is returned by the computeCovarianceMatrix function. * \param[in] cloud the input point cloud * \param[in] centroid the centroid of the set of points in the cloud * \param[out] covariance_matrix the resultant 3x3 covariance matrix - * \return number of valid point used to determine the covariance matrix. + * \return number of valid points used to determine the covariance matrix. * In case of dense point clouds, this is the same as the size of input cloud. * \ingroup common */ @@ -239,13 +239,13 @@ namespace pcl * The result is returned as a Eigen::Matrix3f. * Note: the covariance matrix is not normalized with the number of * points. For a normalized covariance, please use - * computeNormalizedCovarianceMatrix. + * computeCovarianceMatrixNormalized. * \param[in] cloud the input point cloud * \param[in] indices the point cloud indices that need to be used * \param[in] centroid the centroid of the set of points in the cloud * \param[out] covariance_matrix the resultant 3x3 covariance matrix - * \return number of valid point used to determine the covariance matrix. - * In case of dense point clouds, this is the same as the size of input cloud. + * \return number of valid points used to determine the covariance matrix. + * In case of dense point clouds, this is the same as the size of input indices. * \ingroup common */ template inline unsigned int @@ -276,13 +276,13 @@ namespace pcl * The result is returned as a Eigen::Matrix3f. * Note: the covariance matrix is not normalized with the number of * points. For a normalized covariance, please use - * computeNormalizedCovarianceMatrix. + * computeCovarianceMatrixNormalized. * \param[in] cloud the input point cloud * \param[in] indices the point cloud indices that need to be used * \param[in] centroid the centroid of the set of points in the cloud * \param[out] covariance_matrix the resultant 3x3 covariance matrix - * \return number of valid point used to determine the covariance matrix. - * In case of dense point clouds, this is the same as the size of input cloud. + * \return number of valid points used to determine the covariance matrix. + * In case of dense point clouds, this is the same as the size of input indices. * \ingroup common */ template inline unsigned int @@ -313,15 +313,15 @@ namespace pcl * their indices. * The result is returned as a Eigen::Matrix3f. * Normalized means that every entry has been divided by the number of entries in indices. - * For small number of points, or if you want explicitely the sample-variance, use computeCovarianceMatrix + * For small number of points, or if you want explicitly the sample-variance, use computeCovarianceMatrix * and scale the covariance matrix with 1 / (n-1), where n is the number of points used to calculate * the covariance matrix and is returned by the computeCovarianceMatrix function. * \param[in] cloud the input point cloud * \param[in] indices the point cloud indices that need to be used * \param[in] centroid the centroid of the set of points in the cloud * \param[out] covariance_matrix the resultant 3x3 covariance matrix - * \return number of valid point used to determine the covariance matrix. - * In case of dense point clouds, this is the same as the size of input cloud. + * \return number of valid points used to determine the covariance matrix. + * In case of dense point clouds, this is the same as the size of input indices. * \ingroup common */ template inline unsigned int @@ -351,15 +351,15 @@ namespace pcl /** \brief Compute the normalized 3x3 covariance matrix of a given set of points using * their indices. The result is returned as a Eigen::Matrix3f. * Normalized means that every entry has been divided by the number of entries in indices. - * For small number of points, or if you want explicitely the sample-variance, use computeCovarianceMatrix + * For small number of points, or if you want explicitly the sample-variance, use computeCovarianceMatrix * and scale the covariance matrix with 1 / (n-1), where n is the number of points used to calculate * the covariance matrix and is returned by the computeCovarianceMatrix function. * \param[in] cloud the input point cloud * \param[in] indices the point cloud indices that need to be used * \param[in] centroid the centroid of the set of points in the cloud * \param[out] covariance_matrix the resultant 3x3 covariance matrix - * \return number of valid point used to determine the covariance matrix. - * In case of dense point clouds, this is the same as the size of input cloud. + * \return number of valid points used to determine the covariance matrix. + * In case of dense point clouds, this is the same as the size of input indices. * \ingroup common */ template inline unsigned int @@ -387,14 +387,14 @@ namespace pcl } /** \brief Compute the normalized 3x3 covariance matrix and the centroid of a given set of points in a single loop. - * Normalized means that every entry has been divided by the number of entries in indices. - * For small number of points, or if you want explicitely the sample-variance, scale the covariance matrix + * Normalized means that every entry has been divided by the number of valid entries in the point cloud. + * For small number of points, or if you want explicitly the sample-variance, scale the covariance matrix * with n / (n-1), where n is the number of points used to calculate the covariance matrix and is returned by this function. * \note This method is theoretically exact. However using float for internal calculations reduces the accuracy but increases the efficiency. * \param[in] cloud the input point cloud * \param[out] covariance_matrix the resultant 3x3 covariance matrix * \param[out] centroid the centroid of the set of points in the cloud - * \return number of valid point used to determine the covariance matrix. + * \return number of valid points used to determine the covariance matrix. * In case of dense point clouds, this is the same as the size of input cloud. * \ingroup common */ @@ -421,15 +421,15 @@ namespace pcl /** \brief Compute the normalized 3x3 covariance matrix and the centroid of a given set of points in a single loop. * Normalized means that every entry has been divided by the number of entries in indices. - * For small number of points, or if you want explicitely the sample-variance, scale the covariance matrix + * For small number of points, or if you want explicitly the sample-variance, scale the covariance matrix * with n / (n-1), where n is the number of points used to calculate the covariance matrix and is returned by this function. * \note This method is theoretically exact. However using float for internal calculations reduces the accuracy but increases the efficiency. * \param[in] cloud the input point cloud * \param[in] indices subset of points given by their indices * \param[out] covariance_matrix the resultant 3x3 covariance matrix * \param[out] centroid the centroid of the set of points in the cloud - * \return number of valid point used to determine the covariance matrix. - * In case of dense point clouds, this is the same as the size of input cloud. + * \return number of valid points used to determine the covariance matrix. + * In case of dense point clouds, this is the same as the size of input indices. * \ingroup common */ template inline unsigned int @@ -458,15 +458,15 @@ namespace pcl /** \brief Compute the normalized 3x3 covariance matrix and the centroid of a given set of points in a single loop. * Normalized means that every entry has been divided by the number of entries in indices. - * For small number of points, or if you want explicitely the sample-variance, scale the covariance matrix + * For small number of points, or if you want explicitly the sample-variance, scale the covariance matrix * with n / (n-1), where n is the number of points used to calculate the covariance matrix and is returned by this function. * \note This method is theoretically exact. However using float for internal calculations reduces the accuracy but increases the efficiency. * \param[in] cloud the input point cloud * \param[in] indices subset of points given by their indices * \param[out] centroid the centroid of the set of points in the cloud * \param[out] covariance_matrix the resultant 3x3 covariance matrix - * \return number of valid point used to determine the covariance matrix. - * In case of dense point clouds, this is the same as the size of input cloud. + * \return number of valid points used to determine the covariance matrix. + * In case of dense point clouds, this is the same as the size of input indices. * \ingroup common */ template inline unsigned int @@ -494,13 +494,13 @@ namespace pcl } /** \brief Compute the normalized 3x3 covariance matrix for a already demeaned point cloud. - * Normalized means that every entry has been divided by the number of entries in indices. - * For small number of points, or if you want explicitely the sample-variance, scale the covariance matrix + * Normalized means that every entry has been divided by the number of entries in the input point cloud. + * For small number of points, or if you want explicitly the sample-variance, scale the covariance matrix * with n / (n-1), where n is the number of points used to calculate the covariance matrix and is returned by this function. * \note This method is theoretically exact. However using float for internal calculations reduces the accuracy but increases the efficiency. * \param[in] cloud the input point cloud * \param[out] covariance_matrix the resultant 3x3 covariance matrix - * \return number of valid point used to determine the covariance matrix. + * \return number of valid points used to determine the covariance matrix. * In case of dense point clouds, this is the same as the size of input cloud. * \ingroup common */ @@ -524,14 +524,14 @@ namespace pcl /** \brief Compute the normalized 3x3 covariance matrix for a already demeaned point cloud. * Normalized means that every entry has been divided by the number of entries in indices. - * For small number of points, or if you want explicitely the sample-variance, scale the covariance matrix + * For small number of points, or if you want explicitly the sample-variance, scale the covariance matrix * with n / (n-1), where n is the number of points used to calculate the covariance matrix and is returned by this function. * \note This method is theoretically exact. However using float for internal calculations reduces the accuracy but increases the efficiency. * \param[in] cloud the input point cloud * \param[in] indices subset of points given by their indices * \param[out] covariance_matrix the resultant 3x3 covariance matrix - * \return number of valid point used to determine the covariance matrix. - * In case of dense point clouds, this is the same as the size of input cloud. + * \return number of valid points used to determine the covariance matrix. + * In case of dense point clouds, this is the same as the size of input indices. * \ingroup common */ template inline unsigned int @@ -557,14 +557,14 @@ namespace pcl /** \brief Compute the normalized 3x3 covariance matrix for a already demeaned point cloud. * Normalized means that every entry has been divided by the number of entries in indices. - * For small number of points, or if you want explicitely the sample-variance, scale the covariance matrix + * For small number of points, or if you want explicitly the sample-variance, scale the covariance matrix * with n / (n-1), where n is the number of points used to calculate the covariance matrix and is returned by this function. * \note This method is theoretically exact. However using float for internal calculations reduces the accuracy but increases the efficiency. * \param[in] cloud the input point cloud * \param[in] indices subset of points given by their indices * \param[out] covariance_matrix the resultant 3x3 covariance matrix - * \return number of valid point used to determine the covariance matrix. - * In case of dense point clouds, this is the same as the size of input cloud. + * \return number of valid points used to determine the covariance matrix. + * In case of dense point clouds, this is the same as the size of input indices. * \ingroup common */ template inline unsigned int diff --git a/common/include/pcl/common/colors.h b/common/include/pcl/common/colors.h index 6fa78e37..419bd978 100644 --- a/common/include/pcl/common/colors.h +++ b/common/include/pcl/common/colors.h @@ -39,19 +39,28 @@ #define PCL_COMMON_COLORS_H #include +#include namespace pcl { - struct RGB; - PCL_EXPORTS RGB getRandomColor (double min = 0.2, double max = 2.8); - /** Color lookup table consisting of 256 colors structured in a maximally - * discontinuous manner. Generated using the method of Glasbey et al. - * (see https://github.com/taketwo/glasbey) */ - class PCL_EXPORTS GlasbeyLUT + enum ColorLUTName + { + /** Color lookup table consisting of 256 colors structured in a maximally + * discontinuous manner. Generated using the method of Glasbey et al. + * (see https://github.com/taketwo/glasbey) */ + LUT_GLASBEY, + /** A perceptually uniform colormap created by Stéfan van der Walt and + * Nathaniel Smith for the Python matplotlib library. + * (see https://youtu.be/xAoljeRJ3lU for background and overview) */ + LUT_VIRIDIS, + }; + + template + class ColorLUT { public: @@ -59,7 +68,7 @@ namespace pcl /** Get a color from the lookup table with a given id. * * The id should be less than the size of the LUT (see size()). */ - static RGB at (unsigned int color_id); + static RGB at (size_t color_id); /** Get the number of colors in the lookup table. * @@ -72,6 +81,9 @@ namespace pcl }; + typedef ColorLUT GlasbeyLUT; + typedef ColorLUT ViridisLUT; + } #endif /* PCL_COMMON_COLORS_H */ diff --git a/common/include/pcl/common/common.h b/common/include/pcl/common/common.h index ef61328c..d9fc4faf 100644 --- a/common/include/pcl/common/common.h +++ b/common/include/pcl/common/common.h @@ -102,8 +102,8 @@ namespace pcl /** \brief Get the point at maximum distance from a given point and a given pointcloud * \param cloud the point cloud data message - * \param pivot_pt the point from where to compute the distance * \param indices the vector of point indices to use from \a cloud + * \param pivot_pt the point from where to compute the distance * \param max_pt the point in cloud that is the farthest point away from pivot_pt * \ingroup common */ diff --git a/common/include/pcl/common/eigen.h b/common/include/pcl/common/eigen.h index 83e0fb95..3869c92f 100644 --- a/common/include/pcl/common/eigen.h +++ b/common/include/pcl/common/eigen.h @@ -238,10 +238,10 @@ namespace pcl * \ingroup common */ inline Eigen::Affine3f - getTransformationFromTwoUnitVectors (const Eigen::Vector3f& y_direction, + getTransformationFromTwoUnitVectors (const Eigen::Vector3f& y_direction, const Eigen::Vector3f& z_axis); - /** \brief Get the transformation that will translate \a orign to (0,0,0) and rotate \a z_axis into (0,0,1) + /** \brief Get the transformation that will translate \a origin to (0,0,0) and rotate \a z_axis into (0,0,1) * and \a y_direction into a vector with x=0 (or into (0,1,0) should \a y_direction be orthogonal to \a z_axis) * \param[in] y_direction the y direction * \param[in] z_axis the z-axis diff --git a/common/include/pcl/common/fft/kiss_fft.h b/common/include/pcl/common/fft/kiss_fft.h index 97487d22..d18e17c5 100644 --- a/common/include/pcl/common/fft/kiss_fft.h +++ b/common/include/pcl/common/fft/kiss_fft.h @@ -6,10 +6,6 @@ #include #include -#if !defined(__APPLE__) -#include -#endif - #include #ifdef __cplusplus diff --git a/common/include/pcl/common/generate.h b/common/include/pcl/common/generate.h index 09291258..1cfd214c 100644 --- a/common/include/pcl/common/generate.h +++ b/common/include/pcl/common/generate.h @@ -64,13 +64,13 @@ namespace pcl /// Default constructor CloudGenerator (); - /** Consttructor with single generator to ensure all X, Y and Z values are within same range - * \param params paramteres for X, Y and Z values generation. Uniqueness is ensured through + /** Constructor with single generator to ensure all X, Y and Z values are within same range + * \param params parameters for X, Y and Z values generation. Uniqueness is ensured through * seed incrementation */ CloudGenerator (const GeneratorParameters& params); - /** Constructor with independant generators per axis + /** Constructor with independent generators per axis * \param x_params parameters for x values generation * \param y_params parameters for y values generation * \param z_params parameters for z values generation @@ -86,19 +86,19 @@ namespace pcl setParameters (const GeneratorParameters& params); /** Set parameters for x values generation - * \param x_params paramters for x values generation + * \param x_params parameters for x values generation */ void setParametersForX (const GeneratorParameters& x_params); /** Set parameters for y values generation - * \param y_params paramters for y values generation + * \param y_params parameters for y values generation */ void setParametersForY (const GeneratorParameters& y_params); /** Set parameters for z values generation - * \param z_params paramters for z values generation + * \param z_params parameters for z values generation */ void setParametersForZ (const GeneratorParameters& z_params); diff --git a/common/include/pcl/common/geometry.h b/common/include/pcl/common/geometry.h index 2c110e7d..dd19237f 100644 --- a/common/include/pcl/common/geometry.h +++ b/common/include/pcl/common/geometry.h @@ -43,6 +43,7 @@ #endif #include +#include /** * \file common/geometry.h @@ -101,6 +102,62 @@ namespace pcl float lambda = plane_normal.dot(po); projected = point - (lambda * plane_normal); } + + + /** \brief Given a plane defined by plane_origin and plane_normal, find the unit vector pointing from plane_origin to the projection of point on the plane. + * + * \param[in] point Point projected on the plane + * \param[in] plane_origin The plane origin + * \param[in] plane_normal The plane normal + * \return unit vector pointing from plane_origin to the projection of point on the plane. + * \ingroup geometry + */ + inline Eigen::Vector3f + projectedAsUnitVector (Eigen::Vector3f const &point, + Eigen::Vector3f const &plane_origin, + Eigen::Vector3f const &plane_normal) + { + Eigen::Vector3f projection; + project (point, plane_origin, plane_normal, projection); + Eigen::Vector3f projected_as_unit_vector = projection - plane_origin; + projected_as_unit_vector.normalize (); + return projected_as_unit_vector; + } + + + /** \brief Define a random unit vector orthogonal to axis. + * + * \param[in] axis Axis + * \return random unit vector orthogonal to axis + * \ingroup geometry + */ + inline Eigen::Vector3f + randomOrthogonalAxis (Eigen::Vector3f const &axis) + { + Eigen::Vector3f rand_ortho_axis; + rand_ortho_axis.setRandom(); + if (std::abs (axis.z ()) > 1E-8f) + { + rand_ortho_axis.z () = -(axis.x () * rand_ortho_axis.x () + axis.y () * rand_ortho_axis.y ()) / axis.z (); + } + else if (std::abs (axis.y ()) > 1E-8f) + { + rand_ortho_axis.y () = -(axis.x () * rand_ortho_axis.x () + axis.z () * rand_ortho_axis.z ()) / axis.y (); + } + else if (std::abs (axis.x ()) > 1E-8f) + { + rand_ortho_axis.x () = -(axis.y () * rand_ortho_axis.y () + axis.z () * rand_ortho_axis.z ()) / axis.x (); + } + else + { + PCL_WARN ("[pcl::randomOrthogonalAxis] provided axis has norm < 1E-8f"); + } + + rand_ortho_axis.normalize (); + return rand_ortho_axis; + } + + } } diff --git a/common/include/pcl/common/impl/bivariate_polynomial.hpp b/common/include/pcl/common/impl/bivariate_polynomial.hpp index 5a979d8a..47234ccd 100644 --- a/common/include/pcl/common/impl/bivariate_polynomial.hpp +++ b/common/include/pcl/common/impl/bivariate_polynomial.hpp @@ -130,7 +130,7 @@ pcl::BivariatePolynomialT::deepCopy (const pcl::BivariatePolynomialT template void pcl::BivariatePolynomialT::calculateGradient (bool forceRecalc) { - if (gradient_x!=NULL && !forceRecalc) + if (gradient_x!=NULL && !forceRecalc) return; if (gradient_x == NULL) gradient_x = new pcl::BivariatePolynomialT (degree-1); diff --git a/common/include/pcl/common/impl/common.hpp b/common/include/pcl/common/impl/common.hpp index 520f2000..ffb8c17e 100644 --- a/common/include/pcl/common/impl/common.hpp +++ b/common/include/pcl/common/impl/common.hpp @@ -70,6 +70,20 @@ pcl::getAngle3D (const Eigen::Vector3f &v1, const Eigen::Vector3f &v2, const boo inline void pcl::getMeanStd (const std::vector &values, double &mean, double &stddev) { + // throw an exception when the input array is empty + if (values.empty ()) + { + PCL_THROW_EXCEPTION (BadArgumentException, "Input array must have at least 1 element."); + } + + // when the array has only one element, mean is the number itself and standard dev is 0 + if (values.size () == 1) + { + mean = values.at (0); + stddev = 0; + return; + } + double sum = 0, sq_sum = 0; for (size_t i = 0; i < values.size (); ++i) diff --git a/common/include/pcl/common/impl/eigen.hpp b/common/include/pcl/common/impl/eigen.hpp index 3f3c00eb..d9005cb7 100644 --- a/common/include/pcl/common/impl/eigen.hpp +++ b/common/include/pcl/common/impl/eigen.hpp @@ -116,7 +116,7 @@ pcl::computeRoots (const Matrix& m, Roots& roots) std::swap (roots (0), roots (1)); } - if (roots (0) <= 0) // eigenval for symetric positive semi-definite matrix can not be negative! Set it to 0 + if (roots (0) <= 0) // eigenval for symmetric positive semi-definite matrix can not be negative! Set it to 0 computeRoots2 (c2, c1, roots); } } @@ -842,6 +842,11 @@ pcl::transformPlane (const Eigen::Matrix &plane_in, plane.coeffs () << plane_in; plane.transform (transformation); plane_out << plane.coeffs (); + + // Versions prior to 3.3.2 don't normalize the result + #if !EIGEN_VERSION_AT_LEAST (3, 3, 2) + plane_out /= plane_out.template head<3> ().norm (); + #endif } ////////////////////////////////////////////////////////////////////////////////////////// @@ -850,7 +855,8 @@ pcl::transformPlane (const pcl::ModelCoefficients::Ptr plane_in, pcl::ModelCoefficients::Ptr plane_out, const Eigen::Transform &transformation) { - Eigen::Matrix < Scalar, 4, 1 > v_plane_in (std::vector < Scalar > (plane_in->values.begin (), plane_in->values.end ()).data ()); + std::vector values (plane_in->values.begin (), plane_in->values.end ()); + Eigen::Matrix < Scalar, 4, 1 > v_plane_in (values.data ()); pcl::transformPlane (v_plane_in, v_plane_in, transformation); plane_out->values.resize (4); for (int i = 0; i < 4; i++) diff --git a/common/include/pcl/common/impl/intersections.hpp b/common/include/pcl/common/impl/intersections.hpp index 1a286ed1..cfc7a67b 100644 --- a/common/include/pcl/common/impl/intersections.hpp +++ b/common/include/pcl/common/impl/intersections.hpp @@ -145,7 +145,7 @@ pcl::threePlanesIntersection (const Eigen::Matrix &plane_a, if (fabs (determinant) < determinant_tolerance) { // det ~= 0 - PCL_DEBUG ("At least two planes are parralel.\n"); + PCL_DEBUG ("At least two planes are parallel.\n"); return (false); } diff --git a/common/include/pcl/common/impl/pca.hpp b/common/include/pcl/common/impl/pca.hpp index 68d5b61c..70ac17a6 100644 --- a/common/include/pcl/common/impl/pca.hpp +++ b/common/include/pcl/common/impl/pca.hpp @@ -45,20 +45,6 @@ #include #include -///////////////////////////////////////////////////////////////////////////////////////// -/** \brief Constructor with direct computation - * \param[in] cloud input m*n matrix (ie n vectors of R(m)) - * \param[in] basis_only flag to compute only the PCA basis - */ -template -pcl::PCA::PCA (const pcl::PointCloud &cloud, bool basis_only) -{ - Base (); - basis_only_ = basis_only; - setInputCloud (cloud.makeShared ()); - compute_done_ = initCompute (); -} - ///////////////////////////////////////////////////////////////////////////////////////// template bool pcl::PCA::initCompute () @@ -66,12 +52,10 @@ pcl::PCA::initCompute () if(!Base::initCompute ()) { PCL_THROW_EXCEPTION (InitFailedException, "[pcl::PCA::initCompute] failed"); - return (false); } if(indices_->size () < 3) { PCL_THROW_EXCEPTION (InitFailedException, "[pcl::PCA::initCompute] number of points < 3"); - return (false); } // Compute mean @@ -82,7 +66,8 @@ pcl::PCA::initCompute () demeanPointCloud (*input_, *indices_, mean_, cloud_demean); assert (cloud_demean.cols () == int (indices_->size ())); // Compute the product cloud_demean * cloud_demean^T - Eigen::Matrix3f alpha = static_cast (cloud_demean.topRows<3> () * cloud_demean.topRows<3> ().transpose ()); + const Eigen::Matrix3f alpha = (1.f / (float (indices_->size ()) - 1.f)) + * cloud_demean.topRows<3> () * cloud_demean.topRows<3> ().transpose (); // Compute eigen vectors and values Eigen::SelfAdjointEigenSolver evd (alpha); @@ -93,7 +78,6 @@ pcl::PCA::initCompute () eigenvectors_.col (i) = evd.eigenvectors ().col (2-i); } // If not basis only then compute the coefficients - if (!basis_only_) coefficients_ = eigenvectors_.transpose() * cloud_demean.topRows<3> (); compute_done_ = true; diff --git a/common/include/pcl/common/impl/polynomial_calculations.hpp b/common/include/pcl/common/impl/polynomial_calculations.hpp index b3673539..891f8aa7 100644 --- a/common/include/pcl/common/impl/polynomial_calculations.hpp +++ b/common/include/pcl/common/impl/polynomial_calculations.hpp @@ -1,3 +1,43 @@ +/* + * Software License Agreement (BSD License) + * + * Point Cloud Library (PCL) - www.pointclouds.org + * Copyright (c) 2010, Willow Garage, Inc. + * Copyright (c) 2012-, Open Perception, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the copyright holder(s) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef PCL_POLYNOMIAL_CALCULATIONS_HPP_ +#define PCL_POLYNOMIAL_CALCULATIONS_HPP_ + //////////////////////////////////// template @@ -15,7 +55,7 @@ pcl::PolynomialCalculationsT:: ~PolynomialCalculationsT () //////////////////////////////////// template -inline void +inline void pcl::PolynomialCalculationsT::Parameters::setZeroValue (real new_zero_value) { zero_value = new_zero_value; @@ -38,7 +78,7 @@ inline void { roots.push_back (-b/a); } - + #if 0 cout << __PRETTY_FUNCTION__ << ": Found "< Calling solveCubicEquation.\n"; solveCubicEquation (b, c, d, e, roots); return; - } + } if (isNearlyZero (e)) { @@ -239,7 +279,7 @@ inline void if (!isNearlyZero (tmpRoots[i])) roots.push_back (tmpRoots[i]); return; - } + } double root1, root2, root3, root4, a2 = a*a, @@ -252,12 +292,12 @@ inline void beta = (b3/ (8.0*a3)) - ( (b*c)/ (2.0*a2)) + (d/a), gamma = ( (-3.0*b4)/ (256.0*a4)) + ( (c*b2)/ (16.0*a3)) - ( (b*d)/ (4.0*a2)) + (e/a), alpha2 = alpha*alpha; - + // Value for resubstitution: double resubValue = b/ (4*a); //cout << "Trying to solve y^4 + "< 0) { w = sqrt (w); @@ -317,7 +357,7 @@ inline void double tmp1 = - (3.0*alpha + 2.0*y + 2.0* (beta/w)), tmp2 = - (3.0*alpha + 2.0*y - 2.0* (beta/w)); - + if (tmp1 > 0) { tmp1 = sqrt (tmp1); @@ -345,10 +385,10 @@ inline void root3 = - (b/ (4.0*a)) - 0.5*w; roots.push_back (root3); } - + //cout << "Test: " << alpha<<", "< samplePoints.size ()) // Too many parameters for this number of equations (points)? { - return false; + return false; // Reduce degree of polynomial //polynomial_degree = (unsigned int) (0.5f* (std::sqrt (8*samplePoints.size ()+1) - 3)); //parameters_size = BivariatePolynomialT::getNoOfParametersFromDegree (polynomial_degree); //cout << "Not enough points, so degree of polynomial was decreased to "< "< A (parameters_size, parameters_size); A.setZero(); @@ -434,20 +474,20 @@ inline bool } tmpX *= currentX; } - + real* APtr = &A(0,0); real* bPtr = &b[0]; real* tmpCPtr1=tmpC; for (unsigned int i=0; i::outProd (tmpC); @@ -489,7 +529,7 @@ inline bool //} //cout << "Calculating matrix A and vector b (size "< parameters; //double choleskyStartTime=-get_time (); //parameters = A.choleskySolve (b); @@ -500,17 +540,17 @@ inline bool //cout << "Inverse took "<< (invStartTime+get_time ())*1000<<"ms.\n"; //cout << PVARC (A)< 1e-5) { //cout << "Inversion result: "<< inversionCheckResult<<" for matrix "< +#endif + +#if defined(__AVX__) +#include +#endif + +namespace pcl +{ + + namespace detail + { + + /** A helper struct to apply an SO3 or SE3 transform to a 3D point. + * Supports single and double precision transform matrices. */ + template + struct Transformer + { + const Eigen::Matrix& tf; + + /** Construct a transformer object. + * The transform matrix is captured by const reference. Make sure that it does not go out of scope before this + * object does. */ + Transformer (const Eigen::Matrix& transform) : tf (transform) { }; + + /** Apply SO3 transform (top-left corner of the transform matrix). + * \param[in] src input 3D point (pointer to 3 floats) + * \param[out] tgt output 3D point (pointer to 4 floats), can be the same as input. The fourth element is set to 0. */ + void so3 (const float* src, float* tgt) const + { + const Scalar p[3] = { src[0], src[1], src[2] }; // need this when src == tgt + tgt[0] = static_cast (tf (0, 0) * p[0] + tf (0, 1) * p[1] + tf (0, 2) * p[2]); + tgt[1] = static_cast (tf (1, 0) * p[0] + tf (1, 1) * p[1] + tf (1, 2) * p[2]); + tgt[2] = static_cast (tf (2, 0) * p[0] + tf (2, 1) * p[1] + tf (2, 2) * p[2]); + tgt[3] = 0; + } + + /** Apply SE3 transform. + * \param[in] src input 3D point (pointer to 3 floats) + * \param[out] tgt output 3D point (pointer to 4 floats), can be the same as input. The fourth element is set to 1. */ + void se3 (const float* src, float* tgt) const + { + const Scalar p[3] = { src[0], src[1], src[2] }; // need this when src == tgt + tgt[0] = static_cast (tf (0, 0) * p[0] + tf (0, 1) * p[1] + tf (0, 2) * p[2] + tf (0, 3)); + tgt[1] = static_cast (tf (1, 0) * p[0] + tf (1, 1) * p[1] + tf (1, 2) * p[2] + tf (1, 3)); + tgt[2] = static_cast (tf (2, 0) * p[0] + tf (2, 1) * p[1] + tf (2, 2) * p[2] + tf (2, 3)); + tgt[3] = 1; + } + }; + +#if defined(__SSE2__) + + /** Optimized version for single-precision transforms using SSE2 intrinsics. */ + template<> + struct Transformer + { + /// Columns of the transform matrix stored in XMM registers. + __m128 c[4]; + + Transformer(const Eigen::Matrix4f& tf) + { + for (size_t i = 0; i < 4; ++i) + c[i] = _mm_load_ps (tf.col (i).data ()); + } + + void so3 (const float* src, float* tgt) const + { + __m128 p0 = _mm_mul_ps (_mm_load_ps1 (&src[0]), c[0]); + __m128 p1 = _mm_mul_ps (_mm_load_ps1 (&src[1]), c[1]); + __m128 p2 = _mm_mul_ps (_mm_load_ps1 (&src[2]), c[2]); + _mm_store_ps (tgt, _mm_add_ps(p0, _mm_add_ps(p1, p2))); + } + + void se3 (const float* src, float* tgt) const + { + __m128 p0 = _mm_mul_ps (_mm_load_ps1 (&src[0]), c[0]); + __m128 p1 = _mm_mul_ps (_mm_load_ps1 (&src[1]), c[1]); + __m128 p2 = _mm_mul_ps (_mm_load_ps1 (&src[2]), c[2]); + _mm_store_ps (tgt, _mm_add_ps(p0, _mm_add_ps(p1, _mm_add_ps(p2, c[3])))); + } + }; + +#if !defined(__AVX__) + + /** Optimized version for double-precision transform using SSE2 intrinsics. */ + template<> + struct Transformer + { + /// Columns of the transform matrix stored in XMM registers. + __m128d c[4][2]; + + Transformer(const Eigen::Matrix4d& tf) + { + for (size_t i = 0; i < 4; ++i) + { + c[i][0] = _mm_load_pd (tf.col (i).data () + 0); + c[i][1] = _mm_load_pd (tf.col (i).data () + 2); + } + } + + void so3 (const float* src, float* tgt) const + { + __m128d xx = _mm_cvtps_pd (_mm_load_ps1 (&src[0])); + __m128d p0 = _mm_mul_pd (xx, c[0][0]); + __m128d p1 = _mm_mul_pd (xx, c[0][1]); + + for (size_t i = 1; i < 3; ++i) + { + __m128d vv = _mm_cvtps_pd (_mm_load_ps1 (&src[i])); + p0 = _mm_add_pd (_mm_mul_pd (vv, c[i][0]), p0); + p1 = _mm_add_pd (_mm_mul_pd (vv, c[i][1]), p1); + } + + _mm_store_ps (tgt, _mm_movelh_ps (_mm_cvtpd_ps (p0), _mm_cvtpd_ps (p1))); + } + + void se3 (const float* src, float* tgt) const + { + __m128d p0 = c[3][0]; + __m128d p1 = c[3][1]; + + for (size_t i = 0; i < 3; ++i) + { + __m128d vv = _mm_cvtps_pd (_mm_load_ps1 (&src[i])); + p0 = _mm_add_pd (_mm_mul_pd (vv, c[i][0]), p0); + p1 = _mm_add_pd (_mm_mul_pd (vv, c[i][1]), p1); + } + + _mm_store_ps (tgt, _mm_movelh_ps (_mm_cvtpd_ps (p0), _mm_cvtpd_ps (p1))); + } + + }; + +#else + + /** Optimized version for double-precision transform using AVX intrinsics. */ + template<> + struct Transformer + { + __m256d c[4]; + + Transformer(const Eigen::Matrix4d& tf) + { + for (size_t i = 0; i < 4; ++i) + c[i] = _mm256_load_pd (tf.col (i).data ()); + } + + void so3 (const float* src, float* tgt) const + { + __m256d p0 = _mm256_mul_pd (_mm256_cvtps_pd (_mm_load_ps1 (&src[0])), c[0]); + __m256d p1 = _mm256_mul_pd (_mm256_cvtps_pd (_mm_load_ps1 (&src[1])), c[1]); + __m256d p2 = _mm256_mul_pd (_mm256_cvtps_pd (_mm_load_ps1 (&src[2])), c[2]); + _mm_store_ps (tgt, _mm256_cvtpd_ps (_mm256_add_pd(p0, _mm256_add_pd(p1, p2)))); + } + + void se3 (const float* src, float* tgt) const + { + __m256d p0 = _mm256_mul_pd (_mm256_cvtps_pd (_mm_load_ps1 (&src[0])), c[0]); + __m256d p1 = _mm256_mul_pd (_mm256_cvtps_pd (_mm_load_ps1 (&src[1])), c[1]); + __m256d p2 = _mm256_mul_pd (_mm256_cvtps_pd (_mm_load_ps1 (&src[2])), c[2]); + _mm_store_ps (tgt, _mm256_cvtpd_ps (_mm256_add_pd(p0, _mm256_add_pd(p1, _mm256_add_pd(p2, c[3]))))); + } + + }; + +#endif +#endif + + } + +} + /////////////////////////////////////////////////////////////////////////////////////////// template void pcl::transformPointCloud (const pcl::PointCloud &cloud_in, @@ -59,17 +232,12 @@ pcl::transformPointCloud (const pcl::PointCloud &cloud_in, cloud_out.sensor_origin_ = cloud_in.sensor_origin_; } + pcl::detail::Transformer tf (transform.matrix ()); if (cloud_in.is_dense) { // If the dataset is dense, simply transform it! for (size_t i = 0; i < cloud_out.points.size (); ++i) - { - //cloud_out.points[i].getVector3fMap () = transform * cloud_in.points[i].getVector3fMap (); - Eigen::Matrix pt (cloud_in[i].x, cloud_in[i].y, cloud_in[i].z); - cloud_out[i].x = static_cast (transform (0, 0) * pt.coeffRef (0) + transform (0, 1) * pt.coeffRef (1) + transform (0, 2) * pt.coeffRef (2) + transform (0, 3)); - cloud_out[i].y = static_cast (transform (1, 0) * pt.coeffRef (0) + transform (1, 1) * pt.coeffRef (1) + transform (1, 2) * pt.coeffRef (2) + transform (1, 3)); - cloud_out[i].z = static_cast (transform (2, 0) * pt.coeffRef (0) + transform (2, 1) * pt.coeffRef (1) + transform (2, 2) * pt.coeffRef (2) + transform (2, 3)); - } + tf.se3 (cloud_in[i].data, cloud_out[i].data); } else { @@ -81,11 +249,7 @@ pcl::transformPointCloud (const pcl::PointCloud &cloud_in, !pcl_isfinite (cloud_in.points[i].y) || !pcl_isfinite (cloud_in.points[i].z)) continue; - //cloud_out.points[i].getVector3fMap () = transform * cloud_in.points[i].getVector3fMap (); - Eigen::Matrix pt (cloud_in[i].x, cloud_in[i].y, cloud_in[i].z); - cloud_out[i].x = static_cast (transform (0, 0) * pt.coeffRef (0) + transform (0, 1) * pt.coeffRef (1) + transform (0, 2) * pt.coeffRef (2) + transform (0, 3)); - cloud_out[i].y = static_cast (transform (1, 0) * pt.coeffRef (0) + transform (1, 1) * pt.coeffRef (1) + transform (1, 2) * pt.coeffRef (2) + transform (1, 3)); - cloud_out[i].z = static_cast (transform (2, 0) * pt.coeffRef (0) + transform (2, 1) * pt.coeffRef (1) + transform (2, 2) * pt.coeffRef (2) + transform (2, 3)); + tf.se3 (cloud_in[i].data, cloud_out[i].data); } } } @@ -108,6 +272,7 @@ pcl::transformPointCloud (const pcl::PointCloud &cloud_in, cloud_out.sensor_orientation_ = cloud_in.sensor_orientation_; cloud_out.sensor_origin_ = cloud_in.sensor_origin_; + pcl::detail::Transformer tf (transform.matrix ()); if (cloud_in.is_dense) { // If the dataset is dense, simply transform it! @@ -116,11 +281,7 @@ pcl::transformPointCloud (const pcl::PointCloud &cloud_in, // Copy fields first, then transform xyz data if (copy_all_fields) cloud_out.points[i] = cloud_in.points[indices[i]]; - //cloud_out.points[i].getVector3fMap () = transform*cloud_out.points[i].getVector3fMap (); - Eigen::Matrix pt (cloud_in[indices[i]].x, cloud_in[indices[i]].y, cloud_in[indices[i]].z); - cloud_out[i].x = static_cast (transform (0, 0) * pt.coeffRef (0) + transform (0, 1) * pt.coeffRef (1) + transform (0, 2) * pt.coeffRef (2) + transform (0, 3)); - cloud_out[i].y = static_cast (transform (1, 0) * pt.coeffRef (0) + transform (1, 1) * pt.coeffRef (1) + transform (1, 2) * pt.coeffRef (2) + transform (1, 3)); - cloud_out[i].z = static_cast (transform (2, 0) * pt.coeffRef (0) + transform (2, 1) * pt.coeffRef (1) + transform (2, 2) * pt.coeffRef (2) + transform (2, 3)); + tf.se3 (cloud_in[indices[i]].data, cloud_out[i].data); } } else @@ -135,11 +296,7 @@ pcl::transformPointCloud (const pcl::PointCloud &cloud_in, !pcl_isfinite (cloud_in.points[indices[i]].y) || !pcl_isfinite (cloud_in.points[indices[i]].z)) continue; - //cloud_out.points[i].getVector3fMap () = transform*cloud_out.points[i].getVector3fMap (); - Eigen::Matrix pt (cloud_in[indices[i]].x, cloud_in[indices[i]].y, cloud_in[indices[i]].z); - cloud_out[i].x = static_cast (transform (0, 0) * pt.coeffRef (0) + transform (0, 1) * pt.coeffRef (1) + transform (0, 2) * pt.coeffRef (2) + transform (0, 3)); - cloud_out[i].y = static_cast (transform (1, 0) * pt.coeffRef (0) + transform (1, 1) * pt.coeffRef (1) + transform (1, 2) * pt.coeffRef (2) + transform (1, 3)); - cloud_out[i].z = static_cast (transform (2, 0) * pt.coeffRef (0) + transform (2, 1) * pt.coeffRef (1) + transform (2, 2) * pt.coeffRef (2) + transform (2, 3)); + tf.se3 (cloud_in[indices[i]].data, cloud_out[i].data); } } } @@ -167,23 +324,14 @@ pcl::transformPointCloudWithNormals (const pcl::PointCloud &cloud_in, cloud_out.sensor_origin_ = cloud_in.sensor_origin_; } + pcl::detail::Transformer tf (transform.matrix ()); // If the data is dense, we don't need to check for NaN if (cloud_in.is_dense) { for (size_t i = 0; i < cloud_out.points.size (); ++i) { - //cloud_out.points[i].getVector3fMap() = transform * cloud_in.points[i].getVector3fMap (); - Eigen::Matrix pt (cloud_in[i].x, cloud_in[i].y, cloud_in[i].z); - cloud_out[i].x = static_cast (transform (0, 0) * pt.coeffRef (0) + transform (0, 1) * pt.coeffRef (1) + transform (0, 2) * pt.coeffRef (2) + transform (0, 3)); - cloud_out[i].y = static_cast (transform (1, 0) * pt.coeffRef (0) + transform (1, 1) * pt.coeffRef (1) + transform (1, 2) * pt.coeffRef (2) + transform (1, 3)); - cloud_out[i].z = static_cast (transform (2, 0) * pt.coeffRef (0) + transform (2, 1) * pt.coeffRef (1) + transform (2, 2) * pt.coeffRef (2) + transform (2, 3)); - - // Rotate normals (WARNING: transform.rotation () uses SVD internally!) - //cloud_out.points[i].getNormalVector3fMap() = transform.rotation () * cloud_in.points[i].getNormalVector3fMap (); - Eigen::Matrix nt (cloud_in[i].normal_x, cloud_in[i].normal_y, cloud_in[i].normal_z); - cloud_out[i].normal_x = static_cast (transform (0, 0) * nt.coeffRef (0) + transform (0, 1) * nt.coeffRef (1) + transform (0, 2) * nt.coeffRef (2)); - cloud_out[i].normal_y = static_cast (transform (1, 0) * nt.coeffRef (0) + transform (1, 1) * nt.coeffRef (1) + transform (1, 2) * nt.coeffRef (2)); - cloud_out[i].normal_z = static_cast (transform (2, 0) * nt.coeffRef (0) + transform (2, 1) * nt.coeffRef (1) + transform (2, 2) * nt.coeffRef (2)); + tf.se3 (cloud_in[i].data, cloud_out[i].data); + tf.so3 (cloud_in[i].data_n, cloud_out[i].data_n); } } // Dataset might contain NaNs and Infs, so check for them first. @@ -195,19 +343,8 @@ pcl::transformPointCloudWithNormals (const pcl::PointCloud &cloud_in, !pcl_isfinite (cloud_in.points[i].y) || !pcl_isfinite (cloud_in.points[i].z)) continue; - - //cloud_out.points[i].getVector3fMap() = transform * cloud_in.points[i].getVector3fMap (); - Eigen::Matrix pt (cloud_in[i].x, cloud_in[i].y, cloud_in[i].z); - cloud_out[i].x = static_cast (transform (0, 0) * pt.coeffRef (0) + transform (0, 1) * pt.coeffRef (1) + transform (0, 2) * pt.coeffRef (2) + transform (0, 3)); - cloud_out[i].y = static_cast (transform (1, 0) * pt.coeffRef (0) + transform (1, 1) * pt.coeffRef (1) + transform (1, 2) * pt.coeffRef (2) + transform (1, 3)); - cloud_out[i].z = static_cast (transform (2, 0) * pt.coeffRef (0) + transform (2, 1) * pt.coeffRef (1) + transform (2, 2) * pt.coeffRef (2) + transform (2, 3)); - - // Rotate normals - //cloud_out.points[i].getNormalVector3fMap() = transform.rotation () * cloud_in.points[i].getNormalVector3fMap (); - Eigen::Matrix nt (cloud_in[i].normal_x, cloud_in[i].normal_y, cloud_in[i].normal_z); - cloud_out[i].normal_x = static_cast (transform (0, 0) * nt.coeffRef (0) + transform (0, 1) * nt.coeffRef (1) + transform (0, 2) * nt.coeffRef (2)); - cloud_out[i].normal_y = static_cast (transform (1, 0) * nt.coeffRef (0) + transform (1, 1) * nt.coeffRef (1) + transform (1, 2) * nt.coeffRef (2)); - cloud_out[i].normal_z = static_cast (transform (2, 0) * nt.coeffRef (0) + transform (2, 1) * nt.coeffRef (1) + transform (2, 2) * nt.coeffRef (2)); + tf.se3 (cloud_in[i].data, cloud_out[i].data); + tf.so3 (cloud_in[i].data_n, cloud_out[i].data_n); } } } @@ -230,6 +367,7 @@ pcl::transformPointCloudWithNormals (const pcl::PointCloud &cloud_in, cloud_out.sensor_orientation_ = cloud_in.sensor_orientation_; cloud_out.sensor_origin_ = cloud_in.sensor_origin_; + pcl::detail::Transformer tf (transform.matrix ()); // If the data is dense, we don't need to check for NaN if (cloud_in.is_dense) { @@ -238,18 +376,8 @@ pcl::transformPointCloudWithNormals (const pcl::PointCloud &cloud_in, // Copy fields first, then transform if (copy_all_fields) cloud_out.points[i] = cloud_in.points[indices[i]]; - //cloud_out.points[i].getVector3fMap() = transform * cloud_in.points[i].getVector3fMap (); - Eigen::Matrix pt (cloud_in[indices[i]].x, cloud_in[indices[i]].y, cloud_in[indices[i]].z); - cloud_out[i].x = static_cast (transform (0, 0) * pt.coeffRef (0) + transform (0, 1) * pt.coeffRef (1) + transform (0, 2) * pt.coeffRef (2) + transform (0, 3)); - cloud_out[i].y = static_cast (transform (1, 0) * pt.coeffRef (0) + transform (1, 1) * pt.coeffRef (1) + transform (1, 2) * pt.coeffRef (2) + transform (1, 3)); - cloud_out[i].z = static_cast (transform (2, 0) * pt.coeffRef (0) + transform (2, 1) * pt.coeffRef (1) + transform (2, 2) * pt.coeffRef (2) + transform (2, 3)); - - // Rotate normals - //cloud_out.points[i].getNormalVector3fMap() = transform.rotation () * cloud_in.points[i].getNormalVector3fMap (); - Eigen::Matrix nt (cloud_in[indices[i]].normal_x, cloud_in[indices[i]].normal_y, cloud_in[indices[i]].normal_z); - cloud_out[i].normal_x = static_cast (transform (0, 0) * nt.coeffRef (0) + transform (0, 1) * nt.coeffRef (1) + transform (0, 2) * nt.coeffRef (2)); - cloud_out[i].normal_y = static_cast (transform (1, 0) * nt.coeffRef (0) + transform (1, 1) * nt.coeffRef (1) + transform (1, 2) * nt.coeffRef (2)); - cloud_out[i].normal_z = static_cast (transform (2, 0) * nt.coeffRef (0) + transform (2, 1) * nt.coeffRef (1) + transform (2, 2) * nt.coeffRef (2)); + tf.se3 (cloud_in[indices[i]].data, cloud_out[i].data); + tf.so3 (cloud_in[indices[i]].data_n, cloud_out[i].data_n); } } // Dataset might contain NaNs and Infs, so check for them first. @@ -266,18 +394,8 @@ pcl::transformPointCloudWithNormals (const pcl::PointCloud &cloud_in, !pcl_isfinite (cloud_in.points[indices[i]].z)) continue; - //cloud_out.points[i].getVector3fMap() = transform * cloud_in.points[i].getVector3fMap (); - Eigen::Matrix pt (cloud_in[indices[i]].x, cloud_in[indices[i]].y, cloud_in[indices[i]].z); - cloud_out[i].x = static_cast (transform (0, 0) * pt.coeffRef (0) + transform (0, 1) * pt.coeffRef (1) + transform (0, 2) * pt.coeffRef (2) + transform (0, 3)); - cloud_out[i].y = static_cast (transform (1, 0) * pt.coeffRef (0) + transform (1, 1) * pt.coeffRef (1) + transform (1, 2) * pt.coeffRef (2) + transform (1, 3)); - cloud_out[i].z = static_cast (transform (2, 0) * pt.coeffRef (0) + transform (2, 1) * pt.coeffRef (1) + transform (2, 2) * pt.coeffRef (2) + transform (2, 3)); - - // Rotate normals - //cloud_out.points[i].getNormalVector3fMap() = transform.rotation () * cloud_in.points[i].getNormalVector3fMap (); - Eigen::Matrix nt (cloud_in[indices[i]].normal_x, cloud_in[indices[i]].normal_y, cloud_in[indices[i]].normal_z); - cloud_out[i].normal_x = static_cast (transform (0, 0) * nt.coeffRef (0) + transform (0, 1) * nt.coeffRef (1) + transform (0, 2) * nt.coeffRef (2)); - cloud_out[i].normal_y = static_cast (transform (1, 0) * nt.coeffRef (0) + transform (1, 1) * nt.coeffRef (1) + transform (1, 2) * nt.coeffRef (2)); - cloud_out[i].normal_z = static_cast (transform (2, 0) * nt.coeffRef (0) + transform (2, 1) * nt.coeffRef (1) + transform (2, 2) * nt.coeffRef (2)); + tf.se3 (cloud_in[indices[i]].data, cloud_out[i].data); + tf.so3 (cloud_in[indices[i]].data_n, cloud_out[i].data_n); } } } @@ -316,8 +434,8 @@ pcl::transformPoint (const PointT &point, const Eigen::Transform &transform) { PointT ret = point; - ret.getVector3fMap () = transform * point.getVector3fMap (); - + pcl::detail::Transformer tf (transform.matrix ()); + tf.se3 (point.data, ret.data); return (ret); } @@ -327,9 +445,9 @@ pcl::transformPointWithNormal (const PointT &point, const Eigen::Transform &transform) { PointT ret = point; - ret.getVector3fMap () = transform * point.getVector3fMap (); - ret.getNormalVector3fMap () = transform.rotation () * point.getNormalVector3fMap (); - + pcl::detail::Transformer tf (transform.matrix ()); + tf.se3 (point.data, ret.data); + tf.so3 (point.data_n, ret.data_n); return (ret); } diff --git a/common/include/pcl/common/intensity.h b/common/include/pcl/common/intensity.h index 673cfe1e..18ede88e 100644 --- a/common/include/pcl/common/intensity.h +++ b/common/include/pcl/common/intensity.h @@ -44,7 +44,7 @@ namespace pcl { namespace common { - /** \brief Intensity field accessor provides access to the inetnsity filed of a PoinT + /** \brief Intensity field accessor provides access to the intensity filed of a PoinT * implementation for specific types should be done in \file pcl/common/impl/intensity.hpp */ template @@ -78,7 +78,7 @@ namespace pcl p.intensity = intensity; } /** \brief subtract value from intensity field - * \param p point for which to modify inetnsity + * \param p point for which to modify intensity * \param[in] value value to be subtracted from point intensity */ inline void @@ -87,7 +87,7 @@ namespace pcl p.intensity -= value; } /** \brief add value to intensity field - * \param p point for which to modify inetnsity + * \param p point for which to modify intensity * \param[in] value value to be added to point intensity */ inline void diff --git a/common/include/pcl/common/intersections.h b/common/include/pcl/common/intersections.h index 7cf30b12..d0b6733b 100644 --- a/common/include/pcl/common/intersections.h +++ b/common/include/pcl/common/intersections.h @@ -112,7 +112,7 @@ namespace pcl } /** \brief Determine the point of intersection of three non-parallel planes by solving the equations. - * \note If using nearly parralel planes you can lower the determinant_tolerance value. This can + * \note If using nearly parallel planes you can lower the determinant_tolerance value. This can * lead to inconsistent results. * If the three planes intersects in a line the point will be anywhere on the line. * \param[in] plane_a are the coefficients of the first plane in the form ax + by + cz + d = 0 diff --git a/common/include/pcl/common/io.h b/common/include/pcl/common/io.h index ec72feda..6779d800 100644 --- a/common/include/pcl/common/io.h +++ b/common/include/pcl/common/io.h @@ -51,7 +51,7 @@ namespace pcl { /** \brief Get the index of a specified field (i.e., dimension/channel) - * \param[in] cloud the the point cloud message + * \param[in] cloud the point cloud message * \param[in] field_name the string defining the field name * \ingroup common */ @@ -66,7 +66,7 @@ namespace pcl } /** \brief Get the index of a specified field (i.e., dimension/channel) - * \param[in] cloud the the point cloud message + * \param[in] cloud the point cloud message * \param[in] field_name the string defining the field name * \param[out] fields a vector to the original \a PCLPointField vector that the raw PointCloud message contains * \ingroup common @@ -100,7 +100,7 @@ namespace pcl getFields (std::vector &fields); /** \brief Get the list of all fields available in a given cloud - * \param[in] cloud the the point cloud message + * \param[in] cloud the point cloud message * \ingroup common */ template inline std::string @@ -174,12 +174,14 @@ namespace pcl return (pcl::PCLPointField::INT8); if (type == 'U') return (pcl::PCLPointField::UINT8); + break; case 2: if (type == 'I') return (pcl::PCLPointField::INT16); if (type == 'U') return (pcl::PCLPointField::UINT16); + break; case 4: if (type == 'I') @@ -188,13 +190,14 @@ namespace pcl return (pcl::PCLPointField::UINT32); if (type == 'F') return (pcl::PCLPointField::FLOAT32); + break; case 8: - return (pcl::PCLPointField::FLOAT64); - - default: - return (-1); + if (type == 'F') + return (pcl::PCLPointField::FLOAT64); + break; } + return (-1); } /** \brief Obtains the type of the PCLPointField from a specific PCLPointField as a char @@ -413,7 +416,7 @@ namespace pcl * BORDER_REFLECT_101: gfedcb|abcdefgh|gfedcba * BORDER_WRAP: cdefgh|abcdefgh|abcdefg * BORDER_CONSTANT: iiiiii|abcdefgh|iiiiiii with some specified 'i' - * BORDER_TRANSPARENT: mnopqr|abcdefgh|tuvwxyz where m-r and t-z are orignal values of cloud_out + * BORDER_TRANSPARENT: mnopqr|abcdefgh|tuvwxyz where m-r and t-z are original values of cloud_out * \param value * \throw pcl::BadArgumentException if any of top, bottom, left or right is negative. * \ingroup common diff --git a/common/include/pcl/common/pca.h b/common/include/pcl/common/pca.h index e9879c79..de71fd0c 100644 --- a/common/include/pcl/common/pca.h +++ b/common/include/pcl/common/pca.h @@ -93,13 +93,6 @@ namespace pcl , mean_ () , eigenvalues_ () {} - - /** \brief Constructor with direct computation - * X input m*n matrix (ie n vectors of R(m)) - * basis_only flag to compute only the PCA basis - */ - PCL_DEPRECATED ("Use PCA (bool basis_only); setInputCloud (X.makeShared ()); instead") - PCA (const pcl::PointCloud& X, bool basis_only = false); /** Copy Constructor * \param[in] pca PCA object @@ -277,7 +270,6 @@ namespace pcl */ inline void reconstruct (const PointCloud& projection, PointCloud& input); - private: inline bool initCompute (); diff --git a/common/include/pcl/common/poses_from_matches.h b/common/include/pcl/common/poses_from_matches.h index fa5c4c9b..45a10d9c 100644 --- a/common/include/pcl/common/poses_from_matches.h +++ b/common/include/pcl/common/poses_from_matches.h @@ -44,7 +44,7 @@ namespace pcl { /** - * \brief calculate 3D transformation based on point correspondencdes + * \brief calculate 3D transformation based on point correspondences * \author Bastian Steder * \ingroup common */ diff --git a/common/include/pcl/common/spring.h b/common/include/pcl/common/spring.h index db7268da..4d980829 100644 --- a/common/include/pcl/common/spring.h +++ b/common/include/pcl/common/spring.h @@ -51,7 +51,7 @@ namespace pcl * custom values. * \param[in] input the input point cloud * \param[out] output the output point cloud - * \param[in] val the point value to be insterted + * \param[in] val the point value to be inserted * \param[in] amount the amount of rows to be added */ template void @@ -63,7 +63,7 @@ namespace pcl * custom values. * \param[in] input the input point cloud * \param[out] output the output point cloud - * \param[in] val the point value to be insterted + * \param[in] val the point value to be inserted * \param[in] amount the amount of columns to be added */ template void diff --git a/common/include/pcl/common/time.h b/common/include/pcl/common/time.h index bf44ecff..093c262e 100644 --- a/common/include/pcl/common/time.h +++ b/common/include/pcl/common/time.h @@ -163,7 +163,7 @@ namespace pcl stop_watch_.reset (); } - /** \brief Notifies the class that the event occured. */ + /** \brief Notifies the class that the event occurred. */ void event () { event_time_queue_.push (stop_watch_.getTimeSeconds ()); diff --git a/common/include/pcl/common/time_trigger.h b/common/include/pcl/common/time_trigger.h index 941f96b9..c4af9b5e 100644 --- a/common/include/pcl/common/time_trigger.h +++ b/common/include/pcl/common/time_trigger.h @@ -70,7 +70,7 @@ namespace pcl /** \brief Destructor. */ ~TimeTrigger (); - /** \brief registeres a callback + /** \brief registers a callback * \param[in] callback callback function to the list of callbacks. signature has to be boost::function * \return connection the connection, which can be used to disable/enable and remove callback from list */ diff --git a/common/include/pcl/common/utils.h b/common/include/pcl/common/utils.h index 179a4bf0..8a7ef864 100644 --- a/common/include/pcl/common/utils.h +++ b/common/include/pcl/common/utils.h @@ -45,7 +45,7 @@ namespace pcl { namespace utils { - /** \brief Check if val1 and val2 are equals to an epsilon extent + /** \brief Check if val1 and val2 are equal to an epsilon extent * \param[in] val1 first number to check * \param[in] val2 second number to check * \param[in] eps epsilon diff --git a/common/include/pcl/common/vector_average.h b/common/include/pcl/common/vector_average.h index 678f26f8..3651c96c 100644 --- a/common/include/pcl/common/vector_average.h +++ b/common/include/pcl/common/vector_average.h @@ -50,7 +50,7 @@ namespace pcl * \ingroup common */ template - class VectorAverage + class VectorAverage { public: //-----CONSTRUCTOR&DESTRUCTOR----- @@ -96,8 +96,11 @@ namespace pcl /** Get the eigenvector corresponding to the smallest eigenvalue */ inline void getEigenVector1 (Eigen::Matrix& eigen_vector1) const; + + EIGEN_MAKE_ALIGNED_OPERATOR_NEW //-----VARIABLES----- + protected: //-----METHODS----- diff --git a/common/include/pcl/console/parse.h b/common/include/pcl/console/parse.h index 00158752..b9afc062 100644 --- a/common/include/pcl/console/parse.h +++ b/common/include/pcl/console/parse.h @@ -56,7 +56,7 @@ namespace pcl * \note find_switch is simply returning find_argument != -1. */ PCL_EXPORTS bool - find_switch (int argc, char** argv, const char* argument_name); + find_switch (int argc, const char * const * argv, const char * argument_name); /** \brief Finds the position of the argument with name "argument_name" in the argument list "argv" * \param[in] argc the number of command line arguments @@ -65,7 +65,7 @@ namespace pcl * \return index of found argument or -1 if arguments do not appear in list */ PCL_EXPORTS int - find_argument (int argc, char** argv, const char* argument_name); + find_argument (int argc, const char * const * argv, const char * argument_name); /** \brief Template version for parsing arguments. Template parameter needs to have input stream operator overloaded! * \param[in] argc the number of command line arguments @@ -75,7 +75,7 @@ namespace pcl * \return index of found argument or -1 if arguments do not appear in list */ template int - parse (int argc, char** argv, const char* argument_name, Type& value) + parse (int argc, const char * const * argv, const char * argument_name, Type& value) { int index = find_argument (argc, argv, argument_name) + 1; @@ -98,7 +98,7 @@ namespace pcl * \return index of found argument or -1 if arguments do not appear in list */ PCL_EXPORTS int - parse_argument (int argc, char** argv, const char* str, std::string &val); + parse_argument (int argc, const char * const * argv, const char * str, std::string &val); /** \brief Parse for a specific given command line argument. * \param[in] argc the number of command line arguments @@ -108,7 +108,7 @@ namespace pcl * \return index of found argument or -1 if arguments do not appear in list */ PCL_EXPORTS int - parse_argument (int argc, char** argv, const char* str, bool &val); + parse_argument (int argc, const char * const * argv, const char * str, bool &val); /** \brief Parse for a specific given command line argument. * \param[in] argc the number of command line arguments @@ -118,7 +118,7 @@ namespace pcl * \return index of found argument or -1 if arguments do not appear in list */ PCL_EXPORTS int - parse_argument (int argc, char** argv, const char* str, float &val); + parse_argument (int argc, const char * const * argv, const char * str, float &val); /** \brief Parse for a specific given command line argument. * \param[in] argc the number of command line arguments @@ -128,7 +128,7 @@ namespace pcl * \return index of found argument or -1 if arguments do not appear in list */ PCL_EXPORTS int - parse_argument (int argc, char** argv, const char* str, double &val); + parse_argument (int argc, const char * const * argv, const char * str, double &val); /** \brief Parse for a specific given command line argument. * \param[in] argc the number of command line arguments @@ -138,7 +138,7 @@ namespace pcl * \return index of found argument or -1 if arguments do not appear in list */ PCL_EXPORTS int - parse_argument (int argc, char** argv, const char* str, int &val); + parse_argument (int argc, const char * const * argv, const char * str, int &val); /** \brief Parse for a specific given command line argument. * \param[in] argc the number of command line arguments @@ -148,7 +148,7 @@ namespace pcl * \return index of found argument or -1 if arguments do not appear in list */ PCL_EXPORTS int - parse_argument (int argc, char** argv, const char* str, unsigned int &val); + parse_argument (int argc, const char * const * argv, const char * str, unsigned int &val); /** \brief Parse for a specific given command line argument. * \param[in] argc the number of command line arguments @@ -158,7 +158,7 @@ namespace pcl * \return index of found argument or -1 if arguments do not appear in list */ PCL_EXPORTS int - parse_argument (int argc, char** argv, const char* str, char &val); + parse_argument (int argc, const char * const * argv, const char * str, char &val); /** \brief Parse for specific given command line arguments (2x values comma * separated). @@ -171,7 +171,7 @@ namespace pcl * \return index of found argument or -1 if arguments do not appear in list */ PCL_EXPORTS int - parse_2x_arguments (int argc, char** argv, const char* str, float &f, float &s, bool debug = true); + parse_2x_arguments (int argc, const char * const * argv, const char * str, float &f, float &s, bool debug = true); /** \brief Parse for specific given command line arguments (2x values comma * separated). @@ -184,7 +184,7 @@ namespace pcl * \return index of found argument or -1 if arguments do not appear in list */ PCL_EXPORTS int - parse_2x_arguments (int argc, char** argv, const char* str, double &f, double &s, bool debug = true); + parse_2x_arguments (int argc, const char * const * argv, const char * str, double &f, double &s, bool debug = true); /** \brief Parse for specific given command line arguments (2x values comma * separated). @@ -197,7 +197,7 @@ namespace pcl * \return index of found argument or -1 if arguments do not appear in list */ PCL_EXPORTS int - parse_2x_arguments (int argc, char** argv, const char* str, int &f, int &s, bool debug = true); + parse_2x_arguments (int argc, const char * const * argv, const char * str, int &f, int &s, bool debug = true); /** \brief Parse for specific given command line arguments (3x values comma * separated). @@ -211,7 +211,7 @@ namespace pcl * \return index of found argument or -1 if arguments do not appear in list */ PCL_EXPORTS int - parse_3x_arguments (int argc, char** argv, const char* str, float &f, float &s, float &t, bool debug = true); + parse_3x_arguments (int argc, const char * const * argv, const char * str, float &f, float &s, float &t, bool debug = true); /** \brief Parse for specific given command line arguments (3x values comma * separated). @@ -225,7 +225,7 @@ namespace pcl * \return index of found argument or -1 if arguments do not appear in list */ PCL_EXPORTS int - parse_3x_arguments (int argc, char** argv, const char* str, double &f, double &s, double &t, bool debug = true); + parse_3x_arguments (int argc, const char * const * argv, const char * str, double &f, double &s, double &t, bool debug = true); /** \brief Parse for specific given command line arguments (3x values comma * separated). @@ -239,7 +239,7 @@ namespace pcl * return index of found argument or -1 if arguments do not appear in list */ PCL_EXPORTS int - parse_3x_arguments (int argc, char** argv, const char* str, int &f, int &s, int &t, bool debug = true); + parse_3x_arguments (int argc, const char * const * argv, const char * str, int &f, int &s, int &t, bool debug = true); /** \brief Parse for specific given command line arguments (3x values comma * separated). @@ -250,7 +250,7 @@ namespace pcl * \return index of found argument or -1 if arguments do not appear in list */ PCL_EXPORTS int - parse_x_arguments (int argc, char** argv, const char* str, std::vector& v); + parse_x_arguments (int argc, const char * const * argv, const char * str, std::vector& v); /** \brief Parse for specific given command line arguments (N values comma * separated). @@ -261,7 +261,7 @@ namespace pcl * \return index of found argument or -1 if arguments do not appear in list */ PCL_EXPORTS int - parse_x_arguments (int argc, char** argv, const char* str, std::vector& v); + parse_x_arguments (int argc, const char * const * argv, const char * str, std::vector& v); /** \brief Parse for specific given command line arguments (N values comma * separated). @@ -272,9 +272,9 @@ namespace pcl * \return index of found argument or -1 if arguments do not appear in list */ PCL_EXPORTS int - parse_x_arguments (int argc, char** argv, const char* str, std::vector& v); + parse_x_arguments (int argc, const char * const * argv, const char * str, std::vector& v); - /** \brief Parse for specific given command line arguments (multiple occurences + /** \brief Parse for specific given command line arguments (multiple occurrences * of the same command line parameter). * \param[in] argc the number of command line arguments * \param[in] argv the command line arguments @@ -283,9 +283,9 @@ namespace pcl * \return index of found argument or -1 if arguments do not appear in list */ PCL_EXPORTS bool - parse_multiple_arguments (int argc, char** argv, const char* str, std::vector &values); + parse_multiple_arguments (int argc, const char * const * argv, const char * str, std::vector &values); - /** \brief Parse for specific given command line arguments (multiple occurences + /** \brief Parse for specific given command line arguments (multiple occurrences * of the same command line parameter). * \param[in] argc the number of command line arguments * \param[in] argv the command line arguments @@ -294,9 +294,9 @@ namespace pcl * \return true if found, false otherwise */ PCL_EXPORTS bool - parse_multiple_arguments (int argc, char** argv, const char* str, std::vector &values); + parse_multiple_arguments (int argc, const char * const * argv, const char * str, std::vector &values); - /** \brief Parse for specific given command line arguments (multiple occurences + /** \brief Parse for specific given command line arguments (multiple occurrences * of the same command line parameter). * \param[in] argc the number of command line arguments * \param[in] argv the command line arguments @@ -305,9 +305,9 @@ namespace pcl * \return true if found, false otherwise */ PCL_EXPORTS bool - parse_multiple_arguments (int argc, char** argv, const char* str, std::vector &values); + parse_multiple_arguments (int argc, const char * const * argv, const char * str, std::vector &values); - /** \brief Parse for a specific given command line argument (multiple occurences + /** \brief Parse for a specific given command line argument (multiple occurrences * of the same command line parameter). * \param[in] argc the number of command line arguments * \param[in] argv the command line arguments @@ -316,9 +316,9 @@ namespace pcl * \return true if found, false otherwise */ PCL_EXPORTS bool - parse_multiple_arguments (int argc, char** argv, const char* str, std::vector &values); + parse_multiple_arguments (int argc, const char * const * argv, const char * str, std::vector &values); - /** \brief Parse command line arguments for file names with given extension (multiple occurences + /** \brief Parse command line arguments for file names with given extension (multiple occurrences * of 2x argument groups, separated by commas). * \param[in] argc the number of command line arguments * \param[in] argv the command line arguments @@ -328,11 +328,11 @@ namespace pcl * \return true if found, false otherwise */ PCL_EXPORTS bool - parse_multiple_2x_arguments (int argc, char** argv, const char* str, + parse_multiple_2x_arguments (int argc, const char * const * argv, const char * str, std::vector &values_f, std::vector &values_s); - /** \brief Parse command line arguments for file names with given extension (multiple occurences + /** \brief Parse command line arguments for file names with given extension (multiple occurrences * of 3x argument groups, separated by commas). * \param[in] argc the number of command line arguments * \param[in] argv the command line arguments @@ -343,11 +343,21 @@ namespace pcl * \return true if found, false otherwise */ PCL_EXPORTS bool - parse_multiple_3x_arguments (int argc, char** argv, const char* str, + parse_multiple_3x_arguments (int argc, const char * const * argv, const char * str, std::vector &values_f, std::vector &values_s, std::vector &values_t); + /** \brief Parse command line arguments for file names with given extension vector + * \param[in] argc the number of command line arguments + * \param[in] argv the command line arguments + * \param[in] extensions the extensions to search for + * \return a vector with file names indices + */ + PCL_EXPORTS std::vector + parse_file_extension_argument (int argc, const char * const * argv, + const std::vector &extensions); + /** \brief Parse command line arguments for file names with given extension * \param[in] argc the number of command line arguments * \param[in] argv the command line arguments @@ -355,7 +365,7 @@ namespace pcl * \return a vector with file names indices */ PCL_EXPORTS std::vector - parse_file_extension_argument (int argc, char** argv, const std::string &ext); + parse_file_extension_argument (int argc, const char * const * argv, const std::string &ext); } } diff --git a/common/include/pcl/correspondence.h b/common/include/pcl/correspondence.h index 4dfb53cd..ed01627d 100644 --- a/common/include/pcl/correspondence.h +++ b/common/include/pcl/correspondence.h @@ -50,7 +50,7 @@ namespace pcl { /** \brief Correspondence represents a match between two entities (e.g., points, descriptors, etc). This is - * represesented via the indices of a \a source point and a \a target point, and the distance between them. + * represented via the indices of a \a source point and a \a target point, and the distance between them. * * \author Dirk Holz, Radu B. Rusu, Bastian Steder * \ingroup common @@ -104,7 +104,7 @@ namespace pcl * By default (true), vectors are internally sorted before determining their difference. * If the order of correspondences in \a correspondences_after is not different (has not been changed) * from the order in \b correspondences_before this pre-processing step can be disabled - * in order to gain efficiency. In order to disable pre-sorting set \a presorting_requered to false. + * in order to gain efficiency. In order to disable pre-sorting set \a presorting_required to false. */ void getRejectedQueryIndices (const pcl::Correspondences &correspondences_before, @@ -134,7 +134,7 @@ namespace pcl /** * \brief Representation of a (possible) correspondence between two points (e.g. from feature matching), - * that encode complete 6DOF transoformations. + * that encode complete 6DOF transformations. * \ingroup common */ struct PointCorrespondence6D : public PointCorrespondence3D diff --git a/common/include/pcl/exceptions.h b/common/include/pcl/exceptions.h index 1fc1ac04..485e1e67 100644 --- a/common/include/pcl/exceptions.h +++ b/common/include/pcl/exceptions.h @@ -145,7 +145,7 @@ namespace pcl } ; /** \class IsNotDenseException - * \brief An exception that is thrown when a PointCloud is not dense but is attemped to be used as dense + * \brief An exception that is thrown when a PointCloud is not dense but is attempted to be used as dense */ class IsNotDenseException : public PCLException { @@ -251,7 +251,7 @@ namespace pcl }; /** \class BadArgumentException - * \brief An exception that is thrown when the argments number or type is wrong/unhandled. + * \brief An exception that is thrown when the arguments number or type is wrong/unhandled. */ class BadArgumentException : public PCLException { diff --git a/common/include/pcl/impl/instantiate.hpp b/common/include/pcl/impl/instantiate.hpp index 528f96ce..ea73a0c3 100644 --- a/common/include/pcl/impl/instantiate.hpp +++ b/common/include/pcl/impl/instantiate.hpp @@ -54,7 +54,6 @@ #ifdef PCL_NO_PRECOMPILE -#define PCL_INSTANTIATE_PRODUCT_IMPL(r, product) #define PCL_INSTANTIATE_IMPL(r, TEMPLATE, POINT_TYPE) #define PCL_INSTANTIATE(TEMPLATE, POINT_TYPES) #define PCL_INSTANTIATE_PRODUCT_IMPL(r, product) diff --git a/common/include/pcl/impl/point_types.hpp b/common/include/pcl/impl/point_types.hpp index d9772211..4543fa21 100644 --- a/common/include/pcl/impl/point_types.hpp +++ b/common/include/pcl/impl/point_types.hpp @@ -78,6 +78,9 @@ (pcl::NormalBasedSignature12) \ (pcl::FPFHSignature33) \ (pcl::VFHSignature308) \ + (pcl::GASDSignature512) \ + (pcl::GASDSignature984) \ + (pcl::GASDSignature7992) \ (pcl::GRSDSignature21) \ (pcl::ESFSignature640) \ (pcl::BRISKSignature512) \ @@ -146,6 +149,9 @@ (pcl::NormalBasedSignature12) \ (pcl::FPFHSignature33) \ (pcl::VFHSignature308) \ + (pcl::GASDSignature512) \ + (pcl::GASDSignature984) \ + (pcl::GASDSignature7992) \ (pcl::GRSDSignature21) \ (pcl::ESFSignature640) \ (pcl::BRISKSignature512) \ @@ -342,10 +348,17 @@ namespace pcl r = g = b = 0; a = 255; } - + + inline RGB (uint8_t _r, uint8_t _g, uint8_t _b) + { + r = _r; + g = _g; + b = _b; + a = 255; + } + friend std::ostream& operator << (std::ostream& os, const RGB& p); }; - struct _Intensity { @@ -412,7 +425,7 @@ namespace pcl PCL_EXPORTS std::ostream& operator << (std::ostream& os, const Intensity32u& p); /** \brief A point structure representing the grayscale intensity in single-channel images. - * Intensity is represented as a uint8_t value. + * Intensity is represented as a uint32_t value. * \ingroup common */ struct Intensity32u: public _Intensity32u @@ -649,7 +662,7 @@ namespace pcl data[3] = 1.0f; r = g = b = 0; a = 255; - label = 255; + label = 0; } inline PointXYZRGBL (uint8_t _r, uint8_t _g, uint8_t _b, uint32_t _label) { @@ -1408,6 +1421,42 @@ namespace pcl friend std::ostream& operator << (std::ostream& os, const ESFSignature640& p); }; + PCL_EXPORTS std::ostream& operator << (std::ostream& os, const GASDSignature512& p); + /** \brief A point structure representing the Globally Aligned Spatial Distribution (GASD) shape descriptor. + * \ingroup common + */ + struct GASDSignature512 + { + float histogram[512]; + static int descriptorSize() { return 512; } + + friend std::ostream& operator << (std::ostream& os, const GASDSignature512& p); + }; + + PCL_EXPORTS std::ostream& operator << (std::ostream& os, const GASDSignature984& p); + /** \brief A point structure representing the Globally Aligned Spatial Distribution (GASD) shape and color descriptor. + * \ingroup common + */ + struct GASDSignature984 + { + float histogram[984]; + static int descriptorSize() { return 984; } + + friend std::ostream& operator << (std::ostream& os, const GASDSignature984& p); + }; + + PCL_EXPORTS std::ostream& operator << (std::ostream& os, const GASDSignature7992& p); + /** \brief A point structure representing the Globally Aligned Spatial Distribution (GASD) shape and color descriptor. + * \ingroup common + */ + struct GASDSignature7992 + { + float histogram[7992]; + static int descriptorSize() { return 7992; } + + friend std::ostream& operator << (std::ostream& os, const GASDSignature7992& p); + }; + PCL_EXPORTS std::ostream& operator << (std::ostream& os, const GFPFHSignature16& p); /** \brief A point structure representing the GFPFH descriptor with 16 bins. * \ingroup common @@ -1483,7 +1532,7 @@ namespace pcl union { - /** \brief Diameter of the meaningfull keypoint neighborhood. */ + /** \brief Diameter of the meaningful keypoint neighborhood. */ float scale; float size; }; @@ -1615,7 +1664,7 @@ namespace pcl { inline PointDEM (const _PointDEM &p) { - x = p.x; y = p.y; x = p.z; data[3] = 1.0f; + x = p.x; y = p.y; z = p.z; data[3] = 1.0f; intensity = p.intensity; intensity_variance = p.intensity_variance; height_variance = p.height_variance; diff --git a/common/include/pcl/pcl_base.h b/common/include/pcl/pcl_base.h index f678d99c..92f94720 100644 --- a/common/include/pcl/pcl_base.h +++ b/common/include/pcl/pcl_base.h @@ -61,8 +61,8 @@ namespace pcl typedef boost::shared_ptr > IndicesConstPtr; ///////////////////////////////////////////////////////////////////////////////////////// - /** \brief PCL base class. Implements methods that are used by most PCL algorithms. - * \ingroup common + /** \brief PCL base class. Implements methods that are used by most PCL algorithms. + * \ingroup common */ template class PCLBase @@ -77,7 +77,7 @@ namespace pcl /** \brief Empty constructor. */ PCLBase (); - + /** \brief Copy constructor. */ PCLBase (const PCLBase& base); @@ -87,15 +87,15 @@ namespace pcl input_.reset (); indices_.reset (); } - + /** \brief Provide a pointer to the input dataset * \param[in] cloud the const boost shared pointer to a PointCloud message */ - virtual void + virtual void setInputCloud (const PointCloudConstPtr &cloud); /** \brief Get a pointer to the input point cloud dataset. */ - inline PointCloudConstPtr const + inline PointCloudConstPtr const getInputCloud () const { return (input_); } /** \brief Provide a pointer to the vector of indices that represents the input data. @@ -116,7 +116,7 @@ namespace pcl virtual void setIndices (const PointIndicesConstPtr &indices); - /** \brief Set the indices for the points laying within an interest region of + /** \brief Set the indices for the points laying within an interest region of * the point cloud. * \note you shouldn't call this method on unorganized point clouds! * \param[in] row_start the offset on rows @@ -124,15 +124,15 @@ namespace pcl * \param[in] nb_rows the number of rows to be considered row_start included * \param[in] nb_cols the number of columns to be considered col_start included */ - virtual void + virtual void setIndices (size_t row_start, size_t col_start, size_t nb_rows, size_t nb_cols); /** \brief Get a pointer to the vector of indices used. */ - inline IndicesPtr const + inline IndicesPtr const getIndices () { return (indices_); } /** \brief Get a pointer to the vector of indices used. */ - inline IndicesConstPtr const + inline IndicesConstPtr const getIndices () const { return (indices_); } /** \brief Override PointCloud operator[] to shorten code @@ -140,7 +140,7 @@ namespace pcl * or input_->points[(*indices_)[pos]] * \param[in] pos position in indices_ vector */ - inline const PointT& operator[] (size_t pos) const + inline const PointT& operator[] (size_t pos) const { return ((*input_)[(*indices_)[pos]]); } @@ -158,19 +158,19 @@ namespace pcl /** \brief If no set of indices are given, we construct a set of fake indices that mimic the input PointCloud. */ bool fake_indices_; - /** \brief This method should get called before starting the actual computation. + /** \brief This method should get called before starting the actual computation. * * Internally, initCompute() does the following: * - checks if an input dataset is given, and returns false otherwise * - checks whether a set of input indices has been given. Returns true if yes. * - if no input indices have been given, a fake set is created, which will be used until: - * - either a new set is given via setIndices(), or + * - either a new set is given via setIndices(), or * - a new cloud is given that has a different set of points. This will trigger an update on the set of fake indices */ bool initCompute (); - /** \brief This method should get called after finishing the actual computation. + /** \brief This method should get called after finishing the actual computation. */ bool deinitCompute (); @@ -193,7 +193,7 @@ namespace pcl /** \brief Empty constructor. */ PCLBase (); - + /** \brief destructor. */ virtual ~PCLBase() { @@ -204,12 +204,12 @@ namespace pcl /** \brief Provide a pointer to the input dataset * \param cloud the const boost shared pointer to a PointCloud message */ - void + void setInputCloud (const PCLPointCloud2ConstPtr &cloud); /** \brief Get a pointer to the input point cloud dataset. */ - inline PCLPointCloud2ConstPtr const - getInputCloud () { return (input_); } + inline PCLPointCloud2ConstPtr const + getInputCloud () const { return (input_); } /** \brief Provide a pointer to the vector of indices that represents the input data. * \param[in] indices a pointer to the indices that represent the input data. @@ -224,8 +224,8 @@ namespace pcl setIndices (const PointIndicesConstPtr &indices); /** \brief Get a pointer to the vector of indices used. */ - inline IndicesPtr const - getIndices () { return (indices_); } + inline IndicesPtr const + getIndices () const { return (indices_); } protected: /** \brief The input point cloud dataset. */ diff --git a/common/include/pcl/pcl_exports.h b/common/include/pcl/pcl_exports.h index f231c7a1..708216a6 100644 --- a/common/include/pcl/pcl_exports.h +++ b/common/include/pcl/pcl_exports.h @@ -36,7 +36,7 @@ #define PCL_EXPORTS_H_ // This header is created to include to NVCC compiled sources. -// Header 'pcl_macros' is not suitable since it inludes , +// Header 'pcl_macros' is not suitable since it includes , // which can't be eaten by nvcc (it's too weak) #if defined WIN32 || defined _WIN32 || defined WINCE || defined __MINGW32__ diff --git a/common/include/pcl/pcl_macros.h b/common/include/pcl/pcl_macros.h index 925da2a2..7a5f78dd 100644 --- a/common/include/pcl/pcl_macros.h +++ b/common/include/pcl/pcl_macros.h @@ -289,6 +289,25 @@ log2f (float x) #define PCLAPI(rettype) PCL_EXTERN_C PCL_EXPORTS rettype PCL_CDECL #endif +// Macro for pragma operator +#if (defined (__GNUC__) || defined(__clang__)) + #define PCL_PRAGMA(x) _Pragma (#x) +#elif _MSC_VER + #define PCL_PRAGMA(x) __pragma (#x) +#else + #define PCL_PRAGMA +#endif + +// Macro for emitting pragma warning +#if (defined (__GNUC__) || defined(__clang__)) + #define PCL_PRAGMA_WARNING(x) PCL_PRAGMA (GCC warning x) +#elif _MSC_VER + #define PCL_PRAGMA_WARNING(x) PCL_PRAGMA (warning (x)) +#else + #define PCL_PRAGMA_WARNING +#endif + + // Macro to deprecate old functions // // Usage: @@ -302,22 +321,19 @@ log2f (float x) #define __has_extension(x) 0 // Compatibility with pre-3.0 compilers. #endif -#if (defined(__GNUC__) && PCL_LINEAR_VERSION(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) < PCL_LINEAR_VERSION(4,5,0) && ! defined(__clang__)) || defined(__INTEL_COMPILER) -#define PCL_DEPRECATED(message) __attribute__ ((deprecated)) -#endif - +// check Intel compiler first since it usually also defines __GNUC__, __clang__, etc. +#if defined(__INTEL_COMPILER) + #define PCL_DEPRECATED(message) __attribute((deprecated)) +#elif (defined(__GNUC__) && PCL_LINEAR_VERSION(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) < PCL_LINEAR_VERSION(4,5,0) && ! defined(__clang__)) || defined(__INTEL_COMPILER) + #define PCL_DEPRECATED(message) __attribute__ ((deprecated)) // gcc supports this starting from 4.5 : http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43666 -#if (defined(__GNUC__) && PCL_LINEAR_VERSION(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) >= PCL_LINEAR_VERSION(4,5,0)) || (defined(__clang__) && __has_extension(attribute_deprecated_with_message)) -#define PCL_DEPRECATED(message) __attribute__ ((deprecated(message))) -#endif - -#ifdef _MSC_VER -#define PCL_DEPRECATED(message) __declspec(deprecated(message)) -#endif - -#ifndef PCL_DEPRECATED -#pragma message("WARNING: You need to implement PCL_DEPRECATED for this compiler") -#define PCL_DEPRECATED(message) +#elif (defined(__GNUC__) && PCL_LINEAR_VERSION(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) >= PCL_LINEAR_VERSION(4,5,0)) || (defined(__clang__) && __has_extension(attribute_deprecated_with_message)) + #define PCL_DEPRECATED(message) __attribute__ ((deprecated(message))) +#elif defined(_MSC_VER) + #define PCL_DEPRECATED(message) __declspec(deprecated(message)) +#else + #pragma message("WARNING: You need to implement PCL_DEPRECATED for this compiler") + #define PCL_DEPRECATED(message) #endif @@ -337,22 +353,19 @@ log2f (float x) // NewClass() {} // }; -#if (defined(__GNUC__) && PCL_LINEAR_VERSION(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) < PCL_LINEAR_VERSION(4,5,0) && ! defined(__clang__)) || defined(__INTEL_COMPILER) -#define PCL_DEPRECATED_CLASS(func, message) __attribute__ ((deprecated)) func -#endif - +// check Intel compiler first since it usually also defines __GNUC__, __clang__, etc. +#if defined(__INTEL_COMPILER) + #define PCL_DEPRECATED_CLASS(func, message) __attribute((deprecated)) func +#elif (defined(__GNUC__) && PCL_LINEAR_VERSION(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) < PCL_LINEAR_VERSION(4,5,0) && ! defined(__clang__)) || defined(__INTEL_COMPILER) + #define PCL_DEPRECATED_CLASS(func, message) __attribute__ ((deprecated)) func // gcc supports this starting from 4.5 : http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43666 -#if (defined(__GNUC__) && PCL_LINEAR_VERSION(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) >= PCL_LINEAR_VERSION(4,5,0)) || (defined(__clang__) && __has_extension(attribute_deprecated_with_message)) -#define PCL_DEPRECATED_CLASS(func, message) __attribute__ ((deprecated(message))) func -#endif - -#ifdef _MSC_VER -#define PCL_DEPRECATED_CLASS(func, message) __declspec(deprecated(message)) func -#endif - -#ifndef PCL_DEPRECATED_CLASS -#pragma message("WARNING: You need to implement PCL_DEPRECATED_CLASS for this compiler") -#define PCL_DEPRECATED_CLASS(func) func +#elif (defined(__GNUC__) && PCL_LINEAR_VERSION(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) >= PCL_LINEAR_VERSION(4,5,0)) || (defined(__clang__) && __has_extension(attribute_deprecated_with_message)) + #define PCL_DEPRECATED_CLASS(func, message) __attribute__ ((deprecated(message))) func +#elif defined(_MSC_VER) + #define PCL_DEPRECATED_CLASS(func, message) __declspec(deprecated(message)) func +#else + #pragma message("WARNING: You need to implement PCL_DEPRECATED_CLASS for this compiler") + #define PCL_DEPRECATED_CLASS(func) func #endif #if defined (__GNUC__) || defined (__PGI) || defined (__IBMCPP__) || defined (__SUNPRO_CC) @@ -380,7 +393,12 @@ log2f (float x) #endif #if defined (HAVE_MM_MALLOC) - #include + // Intel compiler defines an incompatible _mm_malloc signature + #if defined(__INTEL_COMPILER) + #include + #else + #include + #endif #endif inline void* diff --git a/common/include/pcl/pcl_tests.h b/common/include/pcl/pcl_tests.h index f9b8f136..5b378542 100644 --- a/common/include/pcl/pcl_tests.h +++ b/common/include/pcl/pcl_tests.h @@ -188,6 +188,39 @@ namespace pcl << "Which is: " << p1.getRGBAVector4i ().transpose (); } + template + ::testing::AssertionResult MetaDataEQ (const char* expr1, + const char* expr2, + const PointCloud1T& p1, + const PointCloud2T& p2) + { + if (!(p1.header == p2.header)) + return ::testing::AssertionFailure () << "Headers are different"; + if (p1.width != p2.width) + return ::testing::AssertionFailure () + << "Value of: " << expr2 << ".width" << std::endl + << " Actual: " << p2.width << std::endl + << "Expected: " << expr1 << ".width" << std::endl + << "Which is: " << p1.width << std::endl; + if (p1.height != p2.height) + return ::testing::AssertionFailure () + << "Value of: " << expr2 << ".height" << std::endl + << " Actual: " << p2.height << std::endl + << "Expected: " << expr1 << ".height" << std::endl + << "Which is: " << p1.height << std::endl; + if (p1.is_dense != p2.is_dense) + return ::testing::AssertionFailure () + << "Value of: " << expr2 << ".is_dense" << std::endl + << " Actual: " << p2.is_dense << std::endl + << "Expected: " << expr1 << ".is_dense" << std::endl + << "Which is: " << p1.is_dense << std::endl; + if (p1.sensor_origin_ != p2.sensor_origin_) + return ::testing::AssertionFailure () << "Sensor origins are different"; + if (p1.sensor_orientation_.coeffs () != p2.sensor_orientation_.coeffs ()) + return ::testing::AssertionFailure () << "Sensor orientations are different"; + return ::testing::AssertionSuccess (); + } + } } @@ -215,7 +248,7 @@ namespace pcl /// Assert that differences between x, y, and z fields in /// two points are each within abs_error. #define ASSERT_XYZ_NEAR(expected, actual, abs_error) \ - EXPECT_PRED_FORMAT3(::pcl::test::internal::XYZNear, \ + ASSERT_PRED_FORMAT3(::pcl::test::internal::XYZNear, \ (expected), (actual), abs_error) /// Expect that each of normal_x, normal_y, and normal_z @@ -241,7 +274,7 @@ namespace pcl /// and normal_z fields in two points are each within /// abs_error. #define ASSERT_NORMAL_NEAR(expected, actual, abs_error) \ - EXPECT_PRED_FORMAT3(::pcl::test::internal::NormalNear, \ + ASSERT_PRED_FORMAT3(::pcl::test::internal::NormalNear, \ (expected), (actual), abs_error) /// Expect that each of r, g, and b fields are equal in @@ -268,4 +301,18 @@ namespace pcl ASSERT_PRED_FORMAT2(::pcl::test::internal::RGBAEQ, \ (expected), (actual)) +/// Assert that the metadata (header, width, height, +/// is_dense, sensor origin and orientation) are equal +/// in two point clouds. +#define ASSERT_METADATA_EQ(expected, actual) \ + ASSERT_PRED_FORMAT2(::pcl::test::internal::MetaDataEQ, \ + expected, actual) + +/// Expect that the metadata (header, width, height, +/// is_dense, sensor origin and orientation) are equal +/// in two point clouds. +#define EXPECT_METADATA_EQ(expected, actual) \ + EXPECT_PRED_FORMAT2(::pcl::test::internal::MetaDataEQ, \ + expected, actual) + #endif diff --git a/common/include/pcl/point_cloud.h b/common/include/pcl/point_cloud.h index f0d01f27..aa1e8f45 100644 --- a/common/include/pcl/point_cloud.h +++ b/common/include/pcl/point_cloud.h @@ -40,7 +40,7 @@ #define PCL_POINT_CLOUD_H_ #ifdef __GNUC__ -#pragma GCC system_header +#pragma GCC system_header #endif #include @@ -64,13 +64,13 @@ namespace pcl // Forward declarations template class PointCloud; typedef std::vector MsgFieldMap; - + /** \brief Helper functor structure for copying data between an Eigen type and a PointT. */ template struct NdCopyEigenPointFunctor { typedef typename traits::POD::type Pod; - + /** \brief Constructor * \param[in] p1 the input Eigen type * \param[out] p2 the output Point type @@ -81,7 +81,7 @@ namespace pcl f_idx_ (0) { } /** \brief Operator. Data copy happens here. */ - template inline void + template inline void operator() () { //boost::fusion::at_key (p2_) = p1_[f_idx_++]; @@ -103,7 +103,7 @@ namespace pcl struct NdCopyPointEigenFunctor { typedef typename traits::POD::type Pod; - + /** \brief Constructor * \param[in] p1 the input Point type * \param[out] p2 the output Eigen type @@ -112,7 +112,7 @@ namespace pcl : p1_ (reinterpret_cast(p1)), p2_ (p2), f_idx_ (0) { } /** \brief Operator. Data copy happens here. */ - template inline void + template inline void operator() () { //p2_[f_idx_++] = boost::fusion::at_key (p1_); @@ -143,10 +143,10 @@ namespace pcl * * \code * pcl::PointCloud cloud; - * cloud.push_back (pcl::PointXYZ (rand (), rand (), rand ())); - * cloud.push_back (pcl::PointXYZ (rand (), rand (), rand ())); - * cloud.push_back (pcl::PointXYZ (rand (), rand (), rand ())); - * cloud.push_back (pcl::PointXYZ (rand (), rand (), rand ())); + * cloud.push_back (pcl::PointXYZ (rand (), rand (), rand ())); + * cloud.push_back (pcl::PointXYZ (rand (), rand (), rand ())); + * cloud.push_back (pcl::PointXYZ (rand (), rand (), rand ())); + * cloud.push_back (pcl::PointXYZ (rand (), rand (), rand ())); * \endcode * * The PointCloud class contains the following elements: @@ -176,7 +176,7 @@ namespace pcl * and \ref height to 0, and the \ref sensor_origin_ and \ref * sensor_orientation_ to identity. */ - PointCloud () : + PointCloud () : header (), points (), width (0), height (0), is_dense (true), sensor_origin_ (Eigen::Vector4f::Zero ()), sensor_orientation_ (Eigen::Quaternionf::Identity ()), mapping_ () @@ -185,8 +185,8 @@ namespace pcl /** \brief Copy constructor (needed by compilers such as Intel C++) * \param[in] pc the cloud to copy into this */ - PointCloud (PointCloud &pc) : - header (), points (), width (0), height (0), is_dense (true), + PointCloud (PointCloud &pc) : + header (), points (), width (0), height (0), is_dense (true), sensor_origin_ (Eigen::Vector4f::Zero ()), sensor_orientation_ (Eigen::Quaternionf::Identity ()), mapping_ () { @@ -196,8 +196,8 @@ namespace pcl /** \brief Copy constructor (needed by compilers such as Intel C++) * \param[in] pc the cloud to copy into this */ - PointCloud (const PointCloud &pc) : - header (), points (), width (0), height (0), is_dense (true), + PointCloud (const PointCloud &pc) : + header (), points (), width (0), height (0), is_dense (true), sensor_origin_ (Eigen::Vector4f::Zero ()), sensor_orientation_ (Eigen::Quaternionf::Identity ()), mapping_ () { @@ -208,7 +208,7 @@ namespace pcl * \param[in] pc the cloud to copy into this * \param[in] indices the subset to copy */ - PointCloud (const PointCloud &pc, + PointCloud (const PointCloud &pc, const std::vector &indices) : header (pc.header), points (indices.size ()), width (indices.size ()), height (1), is_dense (pc.is_dense), sensor_origin_ (pc.sensor_origin_), sensor_orientation_ (pc.sensor_orientation_), @@ -242,7 +242,7 @@ namespace pcl /** \brief Add a point cloud to the current cloud. * \param[in] rhs the cloud to add to the current cloud * \return the new cloud as a concatenation of the current cloud and the new given cloud - */ + */ inline PointCloud& operator += (const PointCloud& rhs) { @@ -267,14 +267,14 @@ namespace pcl /** \brief Add a point cloud to another cloud. * \param[in] rhs the cloud to add to the current cloud * \return the new cloud as a concatenation of the current cloud and the new given cloud - */ + */ inline const PointCloud operator + (const PointCloud& rhs) { return (PointCloud (*this) += rhs); } - - /** \brief Obtain the point given by the (column, row) coordinates. Only works on organized + + /** \brief Obtain the point given by the (column, row) coordinates. Only works on organized * datasets (those that have height != 1). * \param[in] column the column coordinate * \param[in] row the row coordinate @@ -285,10 +285,10 @@ namespace pcl if (this->height > 1) return (points.at (row * this->width + column)); else - throw IsNotDenseException ("Can't use 2D indexing with a unorganized point cloud"); + throw UnorganizedPointCloudException ("Can't use 2D indexing with an unorganized point cloud"); } - /** \brief Obtain the point given by the (column, row) coordinates. Only works on organized + /** \brief Obtain the point given by the (column, row) coordinates. Only works on organized * datasets (those that have height != 1). * \param[in] column the column coordinate * \param[in] row the row coordinate @@ -299,10 +299,10 @@ namespace pcl if (this->height > 1) return (points.at (row * this->width + column)); else - throw IsNotDenseException ("Can't use 2D indexing with a unorganized point cloud"); + throw UnorganizedPointCloudException ("Can't use 2D indexing with an unorganized point cloud"); } - /** \brief Obtain the point given by the (column, row) coordinates. Only works on organized + /** \brief Obtain the point given by the (column, row) coordinates. Only works on organized * datasets (those that have height != 1). * \param[in] column the column coordinate * \param[in] row the row coordinate @@ -313,7 +313,7 @@ namespace pcl return (points[row * this->width + column]); } - /** \brief Obtain the point given by the (column, row) coordinates. Only works on organized + /** \brief Obtain the point given by the (column, row) coordinates. Only works on organized * datasets (those that have height != 1). * \param[in] column the column coordinate * \param[in] row the row coordinate @@ -332,13 +332,13 @@ namespace pcl { return (height > 1); } - + /** \brief Return an Eigen MatrixXf (assumes float values) mapped to the specified dimensions of the PointCloud. * \anchor getMatrixXfMap * \note This method is for advanced users only! Use with care! - * + * * \attention Since 1.4.0, Eigen matrices are forced to Row Major to increase the efficiency of the algorithms in PCL - * This means that the behavior of getMatrixXfMap changed, and is now correctly mapping 1-1 with a PointCloud structure, + * This means that the behavior of getMatrixXfMap changed, and is now correctly mapping 1-1 with a PointCloud structure, * that is: number of points in a cloud = rows in a matrix, number of point dimensions = columns in a matrix * * \param[in] dim the number of dimensions to consider for each point @@ -346,9 +346,9 @@ namespace pcl * \param[in] offset the number of dimensions to skip from the beginning of each point * (stride = offset + dim + x, where x is the number of dimensions to skip from the end of each point) * \note for getting only XYZ coordinates out of PointXYZ use dim=3, stride=4 and offset=0 due to the alignment. - * \attention PointT types are most of the time aligned, so the offsets are not continuous! + * \attention PointT types are most of the time aligned, so the offsets are not continuous! */ - inline Eigen::Map > + inline Eigen::Map > getMatrixXfMap (int dim, int stride, int offset) { if (Eigen::MatrixXf::Flags & Eigen::RowMajorBit) @@ -360,9 +360,9 @@ namespace pcl /** \brief Return an Eigen MatrixXf (assumes float values) mapped to the specified dimensions of the PointCloud. * \anchor getMatrixXfMap * \note This method is for advanced users only! Use with care! - * + * * \attention Since 1.4.0, Eigen matrices are forced to Row Major to increase the efficiency of the algorithms in PCL - * This means that the behavior of getMatrixXfMap changed, and is now correctly mapping 1-1 with a PointCloud structure, + * This means that the behavior of getMatrixXfMap changed, and is now correctly mapping 1-1 with a PointCloud structure, * that is: number of points in a cloud = rows in a matrix, number of point dimensions = columns in a matrix * * \param[in] dim the number of dimensions to consider for each point @@ -370,7 +370,7 @@ namespace pcl * \param[in] offset the number of dimensions to skip from the beginning of each point * (stride = offset + dim + x, where x is the number of dimensions to skip from the end of each point) * \note for getting only XYZ coordinates out of PointXYZ use dim=3, stride=4 and offset=0 due to the alignment. - * \attention PointT types are most of the time aligned, so the offsets are not continuous! + * \attention PointT types are most of the time aligned, so the offsets are not continuous! */ inline const Eigen::Map > getMatrixXfMap (int dim, int stride, int offset) const @@ -378,23 +378,23 @@ namespace pcl if (Eigen::MatrixXf::Flags & Eigen::RowMajorBit) return (Eigen::Map >(reinterpret_cast(const_cast(&points[0]))+offset, points.size (), dim, Eigen::OuterStride<> (stride))); else - return (Eigen::Map >(reinterpret_cast(const_cast(&points[0]))+offset, dim, points.size (), Eigen::OuterStride<> (stride))); + return (Eigen::Map >(reinterpret_cast(const_cast(&points[0]))+offset, dim, points.size (), Eigen::OuterStride<> (stride))); } /** \brief Return an Eigen MatrixXf (assumes float values) mapped to the PointCloud. * \note This method is for advanced users only! Use with care! - * \attention PointT types are most of the time aligned, so the offsets are not continuous! + * \attention PointT types are most of the time aligned, so the offsets are not continuous! * See \ref getMatrixXfMap for more information. */ inline Eigen::Map > - getMatrixXfMap () + getMatrixXfMap () { return (getMatrixXfMap (sizeof (PointT) / sizeof (float), sizeof (PointT) / sizeof (float), 0)); } /** \brief Return an Eigen MatrixXf (assumes float values) mapped to the PointCloud. * \note This method is for advanced users only! Use with care! - * \attention PointT types are most of the time aligned, so the offsets are not continuous! + * \attention PointT types are most of the time aligned, so the offsets are not continuous! * See \ref getMatrixXfMap for more information. */ inline const Eigen::Map > @@ -414,7 +414,7 @@ namespace pcl /** \brief The point cloud height (if organized as an image-structure). */ uint32_t height; - /** \brief True if no points are invalid (e.g., have NaN or Inf values). */ + /** \brief True if no points are invalid (e.g., have NaN or Inf values in any of their floating point fields). */ bool is_dense; /** \brief Sensor acquisition pose (origin/translation). */ @@ -452,8 +452,8 @@ namespace pcl /** \brief Resize the cloud * \param[in] n the new cloud size */ - inline void resize (size_t n) - { + inline void resize (size_t n) + { points.resize (n); if (width * height != n) { @@ -476,7 +476,7 @@ namespace pcl * \note This breaks the organized structure of the cloud by setting the height to 1! * \param[in] pt the point to insert */ - inline void + inline void push_back (const PointT& pt) { points.push_back (pt); @@ -490,7 +490,7 @@ namespace pcl * \param[in] pt the point to insert * \return returns the new position iterator */ - inline iterator + inline iterator insert (iterator position, const PointT& pt) { iterator it = points.insert (position, pt); @@ -505,7 +505,7 @@ namespace pcl * \param[in] n the number of times to insert the point * \param[in] pt the point to insert */ - inline void + inline void insert (iterator position, size_t n, const PointT& pt) { points.insert (position, n, pt); @@ -519,7 +519,7 @@ namespace pcl * \param[in] first where to start inserting the points from * \param[in] last where to stop inserting the points from */ - template inline void + template inline void insert (iterator position, InputIterator first, InputIterator last) { points.insert (position, first, last); @@ -527,15 +527,15 @@ namespace pcl height = 1; } - /** \brief Erase a point in the cloud. + /** \brief Erase a point in the cloud. * \note This breaks the organized structure of the cloud by setting the height to 1! * \param[in] position what data point to erase * \return returns the new position iterator */ - inline iterator + inline iterator erase (iterator position) { - iterator it = points.erase (position); + iterator it = points.erase (position); width = static_cast (points.size ()); height = 1; return (it); @@ -547,7 +547,7 @@ namespace pcl * \param[in] last where to stop erasing points from * \return returns the new position iterator */ - inline iterator + inline iterator erase (iterator first, iterator last) { iterator it = points.erase (first, last); @@ -558,10 +558,11 @@ namespace pcl /** \brief Swap a point cloud with another cloud. * \param[in,out] rhs point cloud to swap this with - */ - inline void + */ + inline void swap (PointCloud &rhs) { + std::swap (header, rhs.header); this->points.swap (rhs.points); std::swap (width, rhs.width); std::swap (height, rhs.height); @@ -571,7 +572,7 @@ namespace pcl } /** \brief Removes all points in a cloud and sets the width and height to 0. */ - inline void + inline void clear () { points.clear (); @@ -584,7 +585,7 @@ namespace pcl * The changes of the returned cloud are not mirrored back to this one. * \return shared pointer to the copy of the cloud */ - inline Ptr + inline Ptr makeShared () const { return Ptr (new PointCloud (*this)); } protected: @@ -609,17 +610,18 @@ namespace pcl template std::ostream& operator << (std::ostream& s, const pcl::PointCloud &p) { + s << "header: " << p.header << std::endl; s << "points[]: " << p.points.size () << std::endl; s << "width: " << p.width << std::endl; s << "height: " << p.height << std::endl; s << "is_dense: " << p.is_dense << std::endl; - s << "sensor origin (xyz): [" << - p.sensor_origin_.x () << ", " << - p.sensor_origin_.y () << ", " << - p.sensor_origin_.z () << "] / orientation (xyzw): [" << - p.sensor_orientation_.x () << ", " << - p.sensor_orientation_.y () << ", " << - p.sensor_orientation_.z () << ", " << + s << "sensor origin (xyz): [" << + p.sensor_origin_.x () << ", " << + p.sensor_origin_.y () << ", " << + p.sensor_origin_.z () << "] / orientation (xyzw): [" << + p.sensor_orientation_.x () << ", " << + p.sensor_orientation_.y () << ", " << + p.sensor_orientation_.z () << ", " << p.sensor_orientation_.w () << "]" << std::endl; return (s); diff --git a/common/include/pcl/point_representation.h b/common/include/pcl/point_representation.h index 20f49480..4b8843d7 100644 --- a/common/include/pcl/point_representation.h +++ b/common/include/pcl/point_representation.h @@ -48,7 +48,7 @@ namespace pcl /** \brief @b PointRepresentation provides a set of methods for converting a point structs/object into an * n-dimensional vector. * \note This is an abstract class. Subclasses must set nr_dimensions_ to the appropriate value in the constructor - * and provide an implemention of the pure virtual copyToFloatArray method. + * and provide an implementation of the pure virtual copyToFloatArray method. * \author Michael Dixon */ template @@ -80,7 +80,7 @@ namespace pcl /** \brief Empty destructor */ virtual ~PointRepresentation () {} - /** \brief Copy point data from input point to a float array. This method must be overriden in all subclasses. + /** \brief Copy point data from input point to a float array. This method must be overridden in all subclasses. * \param[in] p The input point * \param[out] out A pointer to a float array. */ @@ -414,6 +414,22 @@ namespace pcl template <> class DefaultPointRepresentation : public DefaultFeatureRepresentation {}; + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + template <> + class DefaultPointRepresentation : public DefaultFeatureRepresentation + {}; + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + template <> + class DefaultPointRepresentation : public DefaultFeatureRepresentation + {}; + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + template <> + class DefaultPointRepresentation : public DefaultFeatureRepresentation + {}; + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template <> class DefaultPointRepresentation : public PointRepresentation diff --git a/common/include/pcl/point_traits.h b/common/include/pcl/point_traits.h index 7d3d97a1..e6900431 100644 --- a/common/include/pcl/point_traits.h +++ b/common/include/pcl/point_traits.h @@ -61,6 +61,13 @@ namespace pcl { + namespace deprecated + { + /** \class DeprecatedType + * \brief A dummy type to aid in template parameter deprecation + */ + struct T {}; + } namespace fields { @@ -337,7 +344,7 @@ namespace pcl /** \brief Get the value at a specified field in a point * \param[in] pt the point to get the value from * \param[in] field_offset the offset of the field - * \param[out] value the value to retreive + * \param[out] value the value to retrieve */ template inline void getFieldValue (const PointT &pt, size_t field_offset, ValT &value) diff --git a/common/include/pcl/point_types.h b/common/include/pcl/point_types.h index 7e341313..b0c6ddfd 100644 --- a/common/include/pcl/point_types.h +++ b/common/include/pcl/point_types.h @@ -275,6 +275,21 @@ namespace pcl */ struct ESFSignature640; + /** \brief Members: float gasd[512] + * \ingroup common + */ + struct GASDSignature512; + + /** \brief Members: float gasd[984] + * \ingroup common + */ + struct GASDSignature984; + + /** \brief Members: float gasd[7992] + * \ingroup common + */ + struct GASDSignature7992; + /** \brief Members: float histogram[16] * \ingroup common */ @@ -627,6 +642,18 @@ POINT_CLOUD_REGISTER_POINT_STRUCT (pcl::ESFSignature640, (float[640], histogram, esf) ) +POINT_CLOUD_REGISTER_POINT_STRUCT(pcl::GASDSignature512, + (float[512], histogram, gasd) +) + +POINT_CLOUD_REGISTER_POINT_STRUCT(pcl::GASDSignature984, + (float[984], histogram, gasd) +) + +POINT_CLOUD_REGISTER_POINT_STRUCT(pcl::GASDSignature7992, + (float[7992], histogram, gasd) +) + POINT_CLOUD_REGISTER_POINT_STRUCT (pcl::Narf36, (float[36], descriptor, descriptor) ) diff --git a/common/include/pcl/point_types_conversion.h b/common/include/pcl/point_types_conversion.h index bb0a6e78..cc561f9c 100644 --- a/common/include/pcl/point_types_conversion.h +++ b/common/include/pcl/point_types_conversion.h @@ -41,12 +41,15 @@ #include +#include +#include + namespace pcl { - // r,g,b, i values are from 0 to 1 + // r,g,b, i values are from 0 to 255 // h = [0,360] // s, v values are from 0 to 1 - // if s = 0 > h = -1 (undefined) + // if s = 0 => h = 0 /** \brief Convert a XYZRGB point type to a XYZI * \param[in] in the input XYZRGB point @@ -79,7 +82,7 @@ namespace pcl PointRGBtoI (const RGB& in, Intensity8u& out) { - out.intensity = static_cast(std::numeric_limits::max() * 0.299f * static_cast (in.r) + out.intensity = static_cast(0.299f * static_cast (in.r) + 0.587f * static_cast (in.g) + 0.114f * static_cast (in.b)); } @@ -91,7 +94,7 @@ namespace pcl PointRGBtoI (const RGB& in, Intensity32u& out) { - out.intensity = static_cast(static_cast(std::numeric_limits::max()) * 0.299f * static_cast (in.r) + out.intensity = static_cast(0.299f * static_cast (in.r) + 0.587f * static_cast (in.g) + 0.114f * static_cast (in.b)); } @@ -132,8 +135,8 @@ namespace pcl if (out.h < 0.f) out.h += 360.f; } - /** \brief Convert a XYZRGB point type to a XYZHSV - * \param[in] in the input XYZRGB point + /** \brief Convert a XYZRGBA point type to a XYZHSV + * \param[in] in the input XYZRGBA point * \param[out] out the output XYZHSV point * \todo include the A parameter but how? */ @@ -150,7 +153,7 @@ namespace pcl if (max == 0) // division by zero { out.s = 0.f; - out.h = 0.f; // h = -1.f; + out.h = 0.f; return; } @@ -238,7 +241,7 @@ namespace pcl } } - /** \brief Convert a RGB point cloud to a Intensity + /** \brief Convert a RGB point cloud to an Intensity * \param[in] in the input RGB point cloud * \param[out] out the output Intensity point cloud */ @@ -256,7 +259,7 @@ namespace pcl } } - /** \brief Convert a RGB point cloud to a Intensity + /** \brief Convert a RGB point cloud to an Intensity * \param[in] in the input RGB point cloud * \param[out] out the output Intensity point cloud */ @@ -274,7 +277,7 @@ namespace pcl } } - /** \brief Convert a RGB point cloud to a Intensity + /** \brief Convert a RGB point cloud to an Intensity * \param[in] in the input RGB point cloud * \param[out] out the output Intensity point cloud */ @@ -368,7 +371,6 @@ namespace pcl for (size_t u = 0; u < width_; u++) { PointXYZRGBA pt; - pt.a = 0; float depth_ = depth.at (u, v).intensity; if (depth_ == 0) diff --git a/common/include/pcl/range_image/range_image.h b/common/include/pcl/range_image/range_image.h index 79b1d65f..e2d1d2a6 100644 --- a/common/include/pcl/range_image/range_image.h +++ b/common/include/pcl/range_image/range_image.h @@ -349,7 +349,7 @@ namespace pcl getTransformationToWorldSystem () const { return to_world_system_;} /** Getter for the angular resolution of the range image in x direction in radians per pixel. - * Provided for downwards compatability */ + * Provided for downwards compatibility */ inline float getAngularResolution () const { return angular_resolution_x_;} @@ -746,7 +746,7 @@ namespace pcl getViewingDirection (const Eigen::Vector3f& point, Eigen::Vector3f& viewing_direction) const; /** Return a newly created Range image. - * Can be reimplmented in derived classes like RangeImagePlanar to return an image of the same type. */ + * Can be reimplemented in derived classes like RangeImagePlanar to return an image of the same type. */ PCL_EXPORTS virtual RangeImage* getNew () const { return new RangeImage; } @@ -771,9 +771,9 @@ namespace pcl Eigen::Affine3f to_world_system_; /**< Inverse of to_range_image_system_ */ float angular_resolution_x_; /**< Angular resolution of the range image in x direction in radians per pixel */ float angular_resolution_y_; /**< Angular resolution of the range image in y direction in radians per pixel */ - float angular_resolution_x_reciprocal_; /**< 1.0/angular_resolution_x_ - provided for better performace of + float angular_resolution_x_reciprocal_; /**< 1.0/angular_resolution_x_ - provided for better performance of * multiplication compared to division */ - float angular_resolution_y_reciprocal_; /**< 1.0/angular_resolution_y_ - provided for better performace of + float angular_resolution_y_reciprocal_; /**< 1.0/angular_resolution_y_ - provided for better performance of * multiplication compared to division */ int image_offset_x_, image_offset_y_; /**< Position of the top left corner of the range image compared to * an image of full size (360x180 degrees) */ diff --git a/common/include/pcl/range_image/range_image_planar.h b/common/include/pcl/range_image/range_image_planar.h index 90b96bfa..3d2db0c6 100644 --- a/common/include/pcl/range_image/range_image_planar.h +++ b/common/include/pcl/range_image/range_image_planar.h @@ -64,11 +64,11 @@ namespace pcl PCL_EXPORTS virtual ~RangeImagePlanar (); /** Return a newly created RangeImagePlanar. - * Reimplmentation to return an image of the same type. */ + * Reimplementation to return an image of the same type. */ virtual RangeImage* getNew () const { return new RangeImagePlanar; } - /** Copy *this to other. Derived version - also copying additonal RangeImagePlanar members */ + /** Copy *this to other. Derived version - also copying additional RangeImagePlanar members */ PCL_EXPORTS virtual void copyTo (RangeImage& other) const; diff --git a/common/include/pcl/range_image/range_image_spherical.h b/common/include/pcl/range_image/range_image_spherical.h index dfba4d43..82ea6637 100644 --- a/common/include/pcl/range_image/range_image_spherical.h +++ b/common/include/pcl/range_image/range_image_spherical.h @@ -63,7 +63,7 @@ namespace pcl PCL_EXPORTS virtual ~RangeImageSpherical () {} /** Return a newly created RangeImagePlanar. - * Reimplmentation to return an image of the same type. */ + * Reimplementation to return an image of the same type. */ virtual RangeImage* getNew () const { return new RangeImageSpherical; } diff --git a/common/include/pcl/ros/conversions.h b/common/include/pcl/ros/conversions.h deleted file mode 100644 index 450e084f..00000000 --- a/common/include/pcl/ros/conversions.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Software License Agreement (BSD License) - * - * Point Cloud Library (PCL) - www.pointclouds.org - * Copyright (c) 2010-2012, Willow Garage, Inc. - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of the copyright holder(s) nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * $Id$ - * - */ - -#ifndef PCL_ROS_CONVERSIONS_H_ -#define PCL_ROS_CONVERSIONS_H_ - -#ifdef __DEPRECATED -#warning The header is deprecated. please use \ - instead. -#endif - -#include - -namespace pcl -{ - /** \brief Convert a PCLPointCloud2 binary data blob into a pcl::PointCloud object using a field_map. - * \param[in] msg the PCLPointCloud2 binary blob - * \param[out] cloud the resultant pcl::PointCloud - * \param[in] field_map a MsgFieldMap object - * - * \note Use fromROSMsg (PCLPointCloud2, PointCloud) directly or create you - * own MsgFieldMap using: - * - * \code - * MsgFieldMap field_map; - * createMapping (msg.fields, field_map); - * \endcode - */ - template - PCL_DEPRECATED ("pcl::fromROSMsg is deprecated, please use fromPCLPointCloud2 instead.") - void - fromROSMsg (const pcl::PCLPointCloud2& msg, pcl::PointCloud& cloud, - const MsgFieldMap& field_map) - { - fromPCLPointCloud2 (msg, cloud, field_map); - } - - /** \brief Convert a PCLPointCloud2 binary data blob into a pcl::PointCloud object. - * \param[in] msg the PCLPointCloud2 binary blob - * \param[out] cloud the resultant pcl::PointCloud - */ - template - PCL_DEPRECATED ("pcl::fromROSMsg is deprecated, please use fromPCLPointCloud2 instead.") - void - fromROSMsg (const pcl::PCLPointCloud2& msg, pcl::PointCloud& cloud) - { - fromPCLPointCloud2 (msg, cloud); - } - - /** \brief Convert a pcl::PointCloud object to a PCLPointCloud2 binary data blob. - * \param[in] cloud the input pcl::PointCloud - * \param[out] msg the resultant PCLPointCloud2 binary blob - */ - template - PCL_DEPRECATED ("pcl::toROSMsg is deprecated, please use toPCLPointCloud2 instead.") - void - toROSMsg (const pcl::PointCloud& cloud, pcl::PCLPointCloud2& msg) - { - toPCLPointCloud2 (cloud, msg); - } - - /** \brief Copy the RGB fields of a PointCloud into pcl::PCLImage format - * \param[in] cloud the point cloud message - * \param[out] msg the resultant pcl::PCLImage - * CloudT cloud type, CloudT should be akin to pcl::PointCloud - * \note will throw std::runtime_error if there is a problem - */ - template - PCL_DEPRECATED ("pcl::toROSMsg is deprecated, please use toPCLPointCloud2 instead.") - void - toROSMsg (const CloudT& cloud, pcl::PCLImage& msg) - { - toPCLPointCloud2 (cloud, msg); - } - - /** \brief Copy the RGB fields of a PCLPointCloud2 msg into pcl::PCLImage format - * \param cloud the point cloud message - * \param msg the resultant pcl::PCLImage - * will throw std::runtime_error if there is a problem - */ - inline void - PCL_DEPRECATED ("pcl::toROSMsg is deprecated, please use toPCLPointCloud2 instead.") - toROSMsg (const pcl::PCLPointCloud2& cloud, pcl::PCLImage& msg) - { - toPCLPointCloud2 (cloud, msg); - } -} - -#endif //#ifndef PCL_ROS_CONVERSIONS_H_ diff --git a/common/src/colors.cpp b/common/src/colors.cpp index 63dbbf35..1033bd19 100644 --- a/common/src/colors.cpp +++ b/common/src/colors.cpp @@ -38,7 +38,7 @@ #include #include -/// Lookup table +/// Glasbey lookup table static const unsigned char GLASBEY_LUT[] = { 77 , 175, 74 , @@ -299,30 +299,297 @@ static const unsigned char GLASBEY_LUT[] = 117, 143, 207, }; +/// Viridis lookup table +static const unsigned char VIRIDIS_LUT[] = +{ + 68 , 1 , 84 , + 68 , 2 , 85 , + 69 , 3 , 87 , + 69 , 5 , 88 , + 69 , 6 , 90 , + 70 , 8 , 91 , + 70 , 9 , 93 , + 70 , 11 , 94 , + 70 , 12 , 96 , + 71 , 14 , 97 , + 71 , 15 , 98 , + 71 , 17 , 100, + 71 , 18 , 101, + 71 , 20 , 102, + 72 , 21 , 104, + 72 , 22 , 105, + 72 , 24 , 106, + 72 , 25 , 108, + 72 , 26 , 109, + 72 , 28 , 110, + 72 , 29 , 111, + 72 , 30 , 112, + 72 , 32 , 113, + 72 , 33 , 115, + 72 , 34 , 116, + 72 , 36 , 117, + 72 , 37 , 118, + 72 , 38 , 119, + 72 , 39 , 120, + 71 , 41 , 121, + 71 , 42 , 121, + 71 , 43 , 122, + 71 , 44 , 123, + 71 , 46 , 124, + 70 , 47 , 125, + 70 , 48 , 126, + 70 , 49 , 126, + 70 , 51 , 127, + 69 , 52 , 128, + 69 , 53 , 129, + 69 , 54 , 129, + 68 , 56 , 130, + 68 , 57 , 131, + 68 , 58 , 131, + 67 , 59 , 132, + 67 , 60 , 132, + 67 , 62 , 133, + 66 , 63 , 133, + 66 , 64 , 134, + 65 , 65 , 134, + 65 , 66 , 135, + 65 , 67 , 135, + 64 , 69 , 136, + 64 , 70 , 136, + 63 , 71 , 136, + 63 , 72 , 137, + 62 , 73 , 137, + 62 , 74 , 137, + 61 , 75 , 138, + 61 , 77 , 138, + 60 , 78 , 138, + 60 , 79 , 138, + 59 , 80 , 139, + 59 , 81 , 139, + 58 , 82 , 139, + 58 , 83 , 139, + 57 , 84 , 140, + 57 , 85 , 140, + 56 , 86 , 140, + 56 , 87 , 140, + 55 , 88 , 140, + 55 , 89 , 140, + 54 , 91 , 141, + 54 , 92 , 141, + 53 , 93 , 141, + 53 , 94 , 141, + 52 , 95 , 141, + 52 , 96 , 141, + 51 , 97 , 141, + 51 , 98 , 141, + 51 , 99 , 141, + 50 , 100, 142, + 50 , 101, 142, + 49 , 102, 142, + 49 , 103, 142, + 48 , 104, 142, + 48 , 105, 142, + 47 , 106, 142, + 47 , 107, 142, + 47 , 108, 142, + 46 , 109, 142, + 46 , 110, 142, + 45 , 111, 142, + 45 , 112, 142, + 45 , 112, 142, + 44 , 113, 142, + 44 , 114, 142, + 43 , 115, 142, + 43 , 116, 142, + 43 , 117, 142, + 42 , 118, 142, + 42 , 119, 142, + 41 , 120, 142, + 41 , 121, 142, + 41 , 122, 142, + 40 , 123, 142, + 40 , 124, 142, + 40 , 125, 142, + 39 , 126, 142, + 39 , 127, 142, + 38 , 128, 142, + 38 , 129, 142, + 38 , 130, 142, + 37 , 131, 142, + 37 , 131, 142, + 37 , 132, 142, + 36 , 133, 142, + 36 , 134, 142, + 35 , 135, 142, + 35 , 136, 142, + 35 , 137, 142, + 34 , 138, 141, + 34 , 139, 141, + 34 , 140, 141, + 33 , 141, 141, + 33 , 142, 141, + 33 , 143, 141, + 32 , 144, 141, + 32 , 145, 140, + 32 , 146, 140, + 32 , 147, 140, + 31 , 147, 140, + 31 , 148, 140, + 31 , 149, 139, + 31 , 150, 139, + 31 , 151, 139, + 30 , 152, 139, + 30 , 153, 138, + 30 , 154, 138, + 30 , 155, 138, + 30 , 156, 137, + 30 , 157, 137, + 30 , 158, 137, + 30 , 159, 136, + 30 , 160, 136, + 31 , 161, 136, + 31 , 162, 135, + 31 , 163, 135, + 31 , 163, 134, + 32 , 164, 134, + 32 , 165, 134, + 33 , 166, 133, + 33 , 167, 133, + 34 , 168, 132, + 35 , 169, 131, + 35 , 170, 131, + 36 , 171, 130, + 37 , 172, 130, + 38 , 173, 129, + 39 , 174, 129, + 40 , 175, 128, + 41 , 175, 127, + 42 , 176, 127, + 43 , 177, 126, + 44 , 178, 125, + 46 , 179, 124, + 47 , 180, 124, + 48 , 181, 123, + 50 , 182, 122, + 51 , 183, 121, + 53 , 183, 121, + 54 , 184, 120, + 56 , 185, 119, + 57 , 186, 118, + 59 , 187, 117, + 61 , 188, 116, + 62 , 189, 115, + 64 , 190, 114, + 66 , 190, 113, + 68 , 191, 112, + 70 , 192, 111, + 72 , 193, 110, + 73 , 194, 109, + 75 , 194, 108, + 77 , 195, 107, + 79 , 196, 106, + 81 , 197, 105, + 83 , 198, 104, + 85 , 198, 102, + 88 , 199, 101, + 90 , 200, 100, + 92 , 201, 99 , + 94 , 201, 98 , + 96 , 202, 96 , + 98 , 203, 95 , + 101, 204, 94 , + 103, 204, 92 , + 105, 205, 91 , + 108, 206, 90 , + 110, 206, 88 , + 112, 207, 87 , + 115, 208, 85 , + 117, 208, 84 , + 119, 209, 82 , + 122, 210, 81 , + 124, 210, 79 , + 127, 211, 78 , + 129, 212, 76 , + 132, 212, 75 , + 134, 213, 73 , + 137, 213, 72 , + 139, 214, 70 , + 142, 215, 68 , + 144, 215, 67 , + 147, 216, 65 , + 149, 216, 63 , + 152, 217, 62 , + 155, 217, 60 , + 157, 218, 58 , + 160, 218, 57 , + 163, 219, 55 , + 165, 219, 53 , + 168, 220, 51 , + 171, 220, 50 , + 173, 221, 48 , + 176, 221, 46 , + 179, 221, 45 , + 181, 222, 43 , + 184, 222, 41 , + 187, 223, 39 , + 189, 223, 38 , + 192, 223, 36 , + 195, 224, 35 , + 197, 224, 33 , + 200, 225, 32 , + 203, 225, 30 , + 205, 225, 29 , + 208, 226, 28 , + 211, 226, 27 , + 213, 226, 26 , + 216, 227, 25 , + 219, 227, 24 , + 221, 227, 24 , + 224, 228, 24 , + 226, 228, 24 , + 229, 228, 24 , + 232, 229, 25 , + 234, 229, 25 , + 237, 229, 26 , + 239, 230, 27 , + 242, 230, 28 , + 244, 230, 30 , + 247, 230, 31 , + 249, 231, 33 , + 251, 231, 35 , + 254, 231, 36 , +}; + /// Number of colors in Glasbey lookup table -static const unsigned int GLASBEY_LUT_SIZE = sizeof (GLASBEY_LUT) / (sizeof (GLASBEY_LUT[0]) * 3); +static const size_t GLASBEY_LUT_SIZE = sizeof (GLASBEY_LUT) / (sizeof (GLASBEY_LUT[0]) * 3); -pcl::RGB -pcl::GlasbeyLUT::at (unsigned int color_id) +/// Number of colors in Viridis lookup table +static const size_t VIRIDIS_LUT_SIZE = sizeof (VIRIDIS_LUT) / (sizeof (VIRIDIS_LUT[0]) * 3); + +static const unsigned char* LUTS[] = { GLASBEY_LUT, VIRIDIS_LUT }; +static const size_t LUT_SIZES[] = { GLASBEY_LUT_SIZE, VIRIDIS_LUT_SIZE }; + +template pcl::RGB +pcl::ColorLUT::at (size_t color_id) { - assert (color_id < GLASBEY_LUT_SIZE); + assert (color_id < LUT_SIZES[T]); pcl::RGB color; - color.r = GLASBEY_LUT[color_id * 3 + 0]; - color.g = GLASBEY_LUT[color_id * 3 + 1]; - color.b = GLASBEY_LUT[color_id * 3 + 2]; + color.r = LUTS[T][color_id * 3 + 0]; + color.g = LUTS[T][color_id * 3 + 1]; + color.b = LUTS[T][color_id * 3 + 2]; return (color); } -size_t -pcl::GlasbeyLUT::size () +template size_t +pcl::ColorLUT::size () { - return GLASBEY_LUT_SIZE; + return LUT_SIZES[T]; } -const unsigned char* -pcl::GlasbeyLUT::data () +template const unsigned char* +pcl::ColorLUT::data () { - return GLASBEY_LUT; + return LUTS[T]; } pcl::RGB @@ -346,3 +613,6 @@ pcl::getRandomColor (double min, double max) color.b = uint8_t (b * 255.0); return (color); } + +template class PCL_EXPORTS pcl::ColorLUT; +template class PCL_EXPORTS pcl::ColorLUT; diff --git a/common/src/fft/kiss_fft.c b/common/src/fft/kiss_fft.c index 98fc588f..8228b1c7 100644 --- a/common/src/fft/kiss_fft.c +++ b/common/src/fft/kiss_fft.c @@ -15,7 +15,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND #include "pcl/common/fft/_kiss_fft_guts.h" /* The guts header contains all the multiplication and addition macros that are defined for - fixed or floating point complex numbers. It also delares the kf_ internal functions. + fixed or floating point complex numbers. It also declares the kf_ internal functions. */ static void kf_bfly2( diff --git a/common/src/parse.cpp b/common/src/parse.cpp index 308243ae..dd4c6971 100644 --- a/common/src/parse.cpp +++ b/common/src/parse.cpp @@ -44,14 +44,14 @@ //////////////////////////////////////////////////////////////////////////////// bool -pcl::console::find_switch (int argc, char** argv, const char* argument_name) +pcl::console::find_switch (int argc, const char * const * argv, const char * argument_name) { return (find_argument (argc, argv, argument_name) != -1); } //////////////////////////////////////////////////////////////////////////////// int -pcl::console::find_argument (int argc, char** argv, const char* argument_name) +pcl::console::find_argument (int argc, const char * const * argv, const char * argument_name) { for (int i = 1; i < argc; ++i) { @@ -66,7 +66,7 @@ pcl::console::find_argument (int argc, char** argv, const char* argument_name) //////////////////////////////////////////////////////////////////////////////// int -pcl::console::parse_argument (int argc, char** argv, const char* str, std::string &val) +pcl::console::parse_argument (int argc, const char * const * argv, const char * str, std::string &val) { int index = find_argument (argc, argv, str) + 1; if (index > 0 && index < argc ) @@ -77,7 +77,7 @@ pcl::console::parse_argument (int argc, char** argv, const char* str, std::strin //////////////////////////////////////////////////////////////////////////////// int -pcl::console::parse_argument (int argc, char** argv, const char* str, bool &val) +pcl::console::parse_argument (int argc, const char * const * argv, const char * str, bool &val) { int index = find_argument (argc, argv, str) + 1; @@ -89,7 +89,7 @@ pcl::console::parse_argument (int argc, char** argv, const char* str, bool &val) //////////////////////////////////////////////////////////////////////////////// int -pcl::console::parse_argument (int argc, char** argv, const char* str, double &val) +pcl::console::parse_argument (int argc, const char * const * argv, const char * str, double &val) { int index = find_argument (argc, argv, str) + 1; @@ -101,7 +101,7 @@ pcl::console::parse_argument (int argc, char** argv, const char* str, double &va //////////////////////////////////////////////////////////////////////////////// int -pcl::console::parse_argument (int argc, char** argv, const char* str, float &val) +pcl::console::parse_argument (int argc, const char * const * argv, const char * str, float &val) { int index = find_argument (argc, argv, str) + 1; @@ -113,7 +113,7 @@ pcl::console::parse_argument (int argc, char** argv, const char* str, float &val //////////////////////////////////////////////////////////////////////////////// int -pcl::console::parse_argument (int argc, char** argv, const char* str, int &val) +pcl::console::parse_argument (int argc, const char * const * argv, const char * str, int &val) { int index = find_argument (argc, argv, str) + 1; @@ -125,7 +125,7 @@ pcl::console::parse_argument (int argc, char** argv, const char* str, int &val) //////////////////////////////////////////////////////////////////////////////// int -pcl::console::parse_argument (int argc, char** argv, const char* str, unsigned int &val) +pcl::console::parse_argument (int argc, const char * const * argv, const char * str, unsigned int &val) { int index = find_argument (argc, argv, str) + 1; @@ -137,7 +137,7 @@ pcl::console::parse_argument (int argc, char** argv, const char* str, unsigned i //////////////////////////////////////////////////////////////////////////////// int -pcl::console::parse_argument (int argc, char** argv, const char* str, char &val) +pcl::console::parse_argument (int argc, const char * const * argv, const char * str, char &val) { int index = find_argument (argc, argv, str) + 1; @@ -149,37 +149,54 @@ pcl::console::parse_argument (int argc, char** argv, const char* str, char &val) //////////////////////////////////////////////////////////////////////////////// std::vector -pcl::console::parse_file_extension_argument (int argc, char** argv, const std::string &extension) +pcl::console::parse_file_extension_argument (int argc, const char * const * argv, + const std::vector &extension) { std::vector indices; for (int i = 1; i < argc; ++i) { std::string fname = std::string (argv[i]); - std::string ext = extension; + for (size_t j = 0; j < extension.size (); ++j) + { + std::string ext = extension[j]; - // Needs to be at least 4: .ext - if (fname.size () <= 4) - continue; + // Needs to be at least 4: .ext + if (fname.size () <= 4) + continue; - // For being case insensitive - std::transform (fname.begin (), fname.end (), fname.begin (), tolower); - std::transform (ext.begin (), ext.end (), ext.begin (), tolower); + // For being case insensitive + std::transform (fname.begin (), fname.end (), fname.begin (), tolower); + std::transform (ext.begin (), ext.end (), ext.begin (), tolower); - // Check if found - std::string::size_type it; - if ((it = fname.rfind (ext)) != std::string::npos) - { - // Additional check: we want to be able to differentiate between .p and .png - if ((ext.size () - (fname.size () - it)) == 0) - indices.push_back (i); + // Check if found + std::string::size_type it; + if ((it = fname.rfind (ext)) != std::string::npos) + { + // Additional check: we want to be able to differentiate between .p and .png + if ((ext.size () - (fname.size () - it)) == 0) + { + indices.push_back (i); + break; + } + } } } return (indices); } +//////////////////////////////////////////////////////////////////////////////// +std::vector +pcl::console::parse_file_extension_argument (int argc, const char * const * argv, + const std::string &ext) +{ + std::vector extensions; + extensions.push_back (ext); + return parse_file_extension_argument (argc, argv, extensions); +} + //////////////////////////////////////////////////////////////////////////////// int -pcl::console::parse_2x_arguments (int argc, char** argv, const char* str, float &f, float &s, bool debug) +pcl::console::parse_2x_arguments (int argc, const char * const * argv, const char * str, float &f, float &s, bool debug) { for (int i = 1; i < argc; ++i) { @@ -204,7 +221,7 @@ pcl::console::parse_2x_arguments (int argc, char** argv, const char* str, float //////////////////////////////////////////////////////////////////////////////// int -pcl::console::parse_2x_arguments (int argc, char** argv, const char* str, double &f, double &s, bool debug) +pcl::console::parse_2x_arguments (int argc, const char * const * argv, const char * str, double &f, double &s, bool debug) { for (int i = 1; i < argc; ++i) { @@ -229,7 +246,7 @@ pcl::console::parse_2x_arguments (int argc, char** argv, const char* str, double //////////////////////////////////////////////////////////////////////////////// int -pcl::console::parse_2x_arguments (int argc, char** argv, const char* str, int &f, int &s, bool debug) +pcl::console::parse_2x_arguments (int argc, const char * const * argv, const char * str, int &f, int &s, bool debug) { for (int i = 1; i < argc; ++i) { @@ -254,7 +271,7 @@ pcl::console::parse_2x_arguments (int argc, char** argv, const char* str, int &f //////////////////////////////////////////////////////////////////////////////// int -pcl::console::parse_3x_arguments (int argc, char** argv, const char* str, float &f, float &s, float &t, bool debug) +pcl::console::parse_3x_arguments (int argc, const char * const * argv, const char * str, float &f, float &s, float &t, bool debug) { for (int i = 1; i < argc; ++i) { @@ -280,7 +297,7 @@ pcl::console::parse_3x_arguments (int argc, char** argv, const char* str, float //////////////////////////////////////////////////////////////////////////////// int -pcl::console::parse_3x_arguments (int argc, char** argv, const char* str, double &f, double &s, double &t, bool debug) +pcl::console::parse_3x_arguments (int argc, const char * const * argv, const char * str, double &f, double &s, double &t, bool debug) { for (int i = 1; i < argc; ++i) { @@ -306,7 +323,7 @@ pcl::console::parse_3x_arguments (int argc, char** argv, const char* str, double //////////////////////////////////////////////////////////////////////////////// int -pcl::console::parse_3x_arguments (int argc, char** argv, const char* str, int &f, int &s, int &t, bool debug) +pcl::console::parse_3x_arguments (int argc, const char * const * argv, const char * str, int &f, int &s, int &t, bool debug) { for (int i = 1; i < argc; ++i) { @@ -332,7 +349,7 @@ pcl::console::parse_3x_arguments (int argc, char** argv, const char* str, int &f //////////////////////////////////////////////////////////////////////////////// int -pcl::console::parse_x_arguments (int argc, char** argv, const char* str, std::vector& v) +pcl::console::parse_x_arguments (int argc, const char * const * argv, const char * str, std::vector& v) { for (int i = 1; i < argc; ++i) { @@ -355,7 +372,7 @@ pcl::console::parse_x_arguments (int argc, char** argv, const char* str, std::ve //////////////////////////////////////////////////////////////////////////////// int -pcl::console::parse_x_arguments (int argc, char** argv, const char* str, std::vector& v) +pcl::console::parse_x_arguments (int argc, const char * const * argv, const char * str, std::vector& v) { for (int i = 1; i < argc; ++i) { @@ -378,7 +395,7 @@ pcl::console::parse_x_arguments (int argc, char** argv, const char* str, std::ve //////////////////////////////////////////////////////////////////////////////// int -pcl::console::parse_x_arguments (int argc, char** argv, const char* str, std::vector& v) +pcl::console::parse_x_arguments (int argc, const char * const * argv, const char * str, std::vector& v) { for (int i = 1; i < argc; ++i) { @@ -401,7 +418,7 @@ pcl::console::parse_x_arguments (int argc, char** argv, const char* str, std::ve //////////////////////////////////////////////////////////////////////////////// bool -pcl::console::parse_multiple_arguments (int argc, char** argv, const char* str, std::vector &values) +pcl::console::parse_multiple_arguments (int argc, const char * const * argv, const char * str, std::vector &values) { for (int i = 1; i < argc; ++i) { @@ -420,7 +437,7 @@ pcl::console::parse_multiple_arguments (int argc, char** argv, const char* str, //////////////////////////////////////////////////////////////////////////////// bool -pcl::console::parse_multiple_arguments (int argc, char** argv, const char* str, std::vector &values) +pcl::console::parse_multiple_arguments (int argc, const char * const * argv, const char * str, std::vector &values) { for (int i = 1; i < argc; ++i) { @@ -439,7 +456,7 @@ pcl::console::parse_multiple_arguments (int argc, char** argv, const char* str, //////////////////////////////////////////////////////////////////////////////// bool -pcl::console::parse_multiple_arguments (int argc, char** argv, const char* str, std::vector &values) +pcl::console::parse_multiple_arguments (int argc, const char * const * argv, const char * str, std::vector &values) { for (int i = 1; i < argc; ++i) { @@ -458,7 +475,7 @@ pcl::console::parse_multiple_arguments (int argc, char** argv, const char* str, //////////////////////////////////////////////////////////////////////////////// bool -pcl::console::parse_multiple_arguments (int argc, char** argv, const char* str, std::vector &values) +pcl::console::parse_multiple_arguments (int argc, const char * const * argv, const char * str, std::vector &values) { for (int i = 1; i < argc; ++i) { @@ -476,7 +493,7 @@ pcl::console::parse_multiple_arguments (int argc, char** argv, const char* str, //////////////////////////////////////////////////////////////////////////////// bool -pcl::console::parse_multiple_2x_arguments (int argc, char** argv, const char* str, std::vector &values_f, std::vector &values_s) +pcl::console::parse_multiple_2x_arguments (int argc, const char * const * argv, const char * str, std::vector &values_f, std::vector &values_s) { double f, s; for (int i = 1; i < argc; ++i) @@ -506,7 +523,7 @@ pcl::console::parse_multiple_2x_arguments (int argc, char** argv, const char* st //////////////////////////////////////////////////////////////////////////////// bool -pcl::console::parse_multiple_3x_arguments (int argc, char** argv, const char* str, +pcl::console::parse_multiple_3x_arguments (int argc, const char * const * argv, const char * str, std::vector &values_f, std::vector &values_s, std::vector &values_t) diff --git a/common/src/point_types.cpp b/common/src/point_types.cpp index b06e99fd..56c3b128 100644 --- a/common/src/point_types.cpp +++ b/common/src/point_types.cpp @@ -70,6 +70,13 @@ namespace pcl return (os); } + std::ostream& + operator << (std::ostream& os, const Intensity32u& p) + { + os << "( " << p.intensity << " )"; + return (os); + } + std::ostream& operator << (std::ostream& os, const PointXYZI& p) { @@ -375,6 +382,30 @@ namespace pcl return (os); } + std::ostream& + operator << (std::ostream& os, const GASDSignature512& p) + { + for (int i = 0; i < 512; ++i) + os << (i == 0 ? "(" : "") << p.histogram[i] << (i < 511 ? ", " : ")"); + return (os); + } + + std::ostream& + operator << (std::ostream& os, const GASDSignature984& p) + { + for (int i = 0; i < 984; ++i) + os << (i == 0 ? "(" : "") << p.histogram[i] << (i < 983 ? ", " : ")"); + return (os); + } + + std::ostream& + operator << (std::ostream& os, const GASDSignature7992& p) + { + for (int i = 0; i < 7992; ++i) + os << (i == 0 ? "(" : "") << p.histogram[i] << (i < 7991 ? ", " : ")"); + return (os); + } + std::ostream& operator << (std::ostream& os, const GFPFHSignature16& p) { diff --git a/common/src/poses_from_matches.cpp b/common/src/poses_from_matches.cpp index d1854326..8e75354e 100644 --- a/common/src/poses_from_matches.cpp +++ b/common/src/poses_from_matches.cpp @@ -95,7 +95,7 @@ pcl::PosesFromMatches::estimatePosesUsing2Correspondences (const pcl::PointCorre pcl::TransformationFromCorrespondences transformation_from_correspondeces; // The following loop structure goes through the pairs in the order 12, 13, 23, 14, 24, 34, ..., - // testing the best correspondences pairs first, without beeing stuck too long with one specific + // testing the best correspondences pairs first, without being stuck too long with one specific // (possibly wrong) correspondence. bool done = false; for (int correspondence2_idx = 0; correspondence2_idx < max_correspondence_idx && !done; ++correspondence2_idx) @@ -209,7 +209,7 @@ pcl::PosesFromMatches::estimatePosesUsing3Correspondences (const PointCorrespond pcl::TransformationFromCorrespondences transformation_from_correspondeces; // The following loop structure goes through the triples in the order 123, 124, 134, 234, 125, 135, 235, ..., - // testing the best correspondences triples first, without beeing stuck too long with one specific + // testing the best correspondences triples first, without being stuck too long with one specific // (possibly wrong) correspondence. bool done = false; for (int correspondence3_idx = 0; correspondence3_idx < max_correspondence_idx && !done; ++correspondence3_idx) diff --git a/cuda/common/include/pcl/cuda/common/eigen.h b/cuda/common/include/pcl/cuda/common/eigen.h index db354f70..f7d3c6bb 100644 --- a/cuda/common/include/pcl/cuda/common/eigen.h +++ b/cuda/common/include/pcl/cuda/common/eigen.h @@ -218,7 +218,7 @@ namespace pcl swap (roots.x, roots.y); } - if (roots.x <= 0.0f) // eigenval for symetric positive semi-definite matrix can not be negative! Set it to 0 + if (roots.x <= 0.0f) // eigenval for symmetric positive semi-definite matrix can not be negative! Set it to 0 computeRoots2 (c2, c1, roots); } } diff --git a/cuda/features/include/pcl/cuda/features/normal_3d_kernels.h b/cuda/features/include/pcl/cuda/features/normal_3d_kernels.h index b5b0858a..10ce06cf 100644 --- a/cuda/features/include/pcl/cuda/features/normal_3d_kernels.h +++ b/cuda/features/include/pcl/cuda/features/normal_3d_kernels.h @@ -164,10 +164,10 @@ namespace pcl , sqr_radius_(sqr_radius) , sqrt_desired_nr_neighbors_ (sqrt_desired_nr_neighbors) {} - + template inline __host__ __device__ - float4 operator () (Tuple &t) + float4 operator () (const Tuple &t) { float3 query_pt = thrust::get<0>(t); float4 normal = thrust::get<1>(t); @@ -190,7 +190,7 @@ namespace pcl (centroid.z - query_pt.z) / sqrt(sqr_radius_) , 0); } - + const PointXYZRGB *points_; float focallength_; OrganizedRadiusSearch search_; diff --git a/cuda/io/src/extract_indices.cu b/cuda/io/src/extract_indices.cu index a56cb143..cb66bd74 100644 --- a/cuda/io/src/extract_indices.cu +++ b/cuda/io/src/extract_indices.cu @@ -156,7 +156,7 @@ struct ColorCloudFromImage template inline __host__ __device__ - PointXYZRGB operator () (Tuple &t) + PointXYZRGB operator () (const Tuple &t) { PointXYZRGB &pt = thrust::get<0>(t); char4 rgb = colors_[thrust::get<1>(t)]; diff --git a/cuda/nn/organized_neighbor_search.hpp b/cuda/nn/organized_neighbor_search.hpp index 29908718..c0a06515 100644 --- a/cuda/nn/organized_neighbor_search.hpp +++ b/cuda/nn/organized_neighbor_search.hpp @@ -190,7 +190,7 @@ namespace pcl // vector for nearest neighbor candidates std::vector nearestNeighbors; - // iterator for radius seach lookup table + // iterator for radius search lookup table typename std::vector::const_iterator radiusSearchLookup_Iterator; radiusSearchLookup_Iterator = radiusSearchLookup_.begin (); diff --git a/cuda/sample_consensus/src/sac_model_1point_plane.cu b/cuda/sample_consensus/src/sac_model_1point_plane.cu index 5b88565e..78cea0fb 100644 --- a/cuda/sample_consensus/src/sac_model_1point_plane.cu +++ b/cuda/sample_consensus/src/sac_model_1point_plane.cu @@ -383,14 +383,14 @@ namespace pcl template bool CountPlanarInlier::operator () (const Tuple &t) { - if (!isfinite (thrust::get<0>(t).x)) + if (!isfinite (thrust::raw_reference_cast(thrust::get<0>(t)).x)) return (false); //TODO: make threshold adaptive, depending on z - return (fabs (thrust::get<0>(t).x * coefficients.x + - thrust::get<0>(t).y * coefficients.y + - thrust::get<0>(t).z * coefficients.z + coefficients.w) < threshold); + return (fabs (thrust::raw_reference_cast(thrust::get<0>(t)).x * coefficients.x + + thrust::raw_reference_cast(thrust::get<0>(t)).y * coefficients.y + + thrust::raw_reference_cast(thrust::get<0>(t)).z * coefficients.z + coefficients.w) < threshold); } ////////////////////////////////////////////////////////////////////////// diff --git a/cuda/sample_consensus/src/sac_model_plane.cu b/cuda/sample_consensus/src/sac_model_plane.cu index faa6710a..49cc4a9b 100644 --- a/cuda/sample_consensus/src/sac_model_plane.cu +++ b/cuda/sample_consensus/src/sac_model_plane.cu @@ -192,12 +192,12 @@ namespace pcl template bool CountPlanarInlier::operator () (const Tuple &t) { - if (!isfinite (thrust::get<0>(t).x)) + if (!isfinite (thrust::raw_reference_cast(thrust::get<0>(t)).x)) return (false); - return (fabs (thrust::get<0>(t).x * coefficients.x + - thrust::get<0>(t).y * coefficients.y + - thrust::get<0>(t).z * coefficients.z + coefficients.w) < threshold); + return (fabs (thrust::raw_reference_cast(thrust::get<0>(t)).x * coefficients.x + + thrust::raw_reference_cast(thrust::get<0>(t)).y * coefficients.y + + thrust::raw_reference_cast(thrust::get<0>(t)).z * coefficients.z + coefficients.w) < threshold); } ////////////////////////////////////////////////////////////////////////// diff --git a/cuda/segmentation/src/mssegmentation.cpp b/cuda/segmentation/src/mssegmentation.cpp index f0e7001d..1ca0e0f3 100644 --- a/cuda/segmentation/src/mssegmentation.cpp +++ b/cuda/segmentation/src/mssegmentation.cpp @@ -162,7 +162,7 @@ void meanShiftSegmentation(const GpuMat& src, Mat& dst, int sp, int sr, int mins vector edges; edges.reserve(g.numv); - // Prepare edges connecting differnet components + // Prepare edges connecting different components for (int v = 0; v < g.numv; ++v) { int c1 = comps.find(v); @@ -174,7 +174,7 @@ void meanShiftSegmentation(const GpuMat& src, Mat& dst, int sp, int sr, int mins } } - // Sort all graph's edges connecting differnet components (in asceding order) + // Sort all graph's edges connecting different components (in ascending order) sort(edges.begin(), edges.end()); // Exclude small components (starting from the nearest couple) diff --git a/doc/advanced/content/conf.py b/doc/advanced/content/conf.py index 84de91dd..22a3803d 100644 --- a/doc/advanced/content/conf.py +++ b/doc/advanced/content/conf.py @@ -6,7 +6,7 @@ import sys, os # -- General configuration ----------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.pngmath', 'sphinxcontrib.doxylink.doxylink'] +extensions = ['sphinx.ext.imgmath', 'sphinxcontrib.doxylink.doxylink'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -129,7 +129,7 @@ html_sidebars = { html_show_copyright = False html_show_sphinx = False html_add_permalinks = None -needs_sphinx = 1.0 +needs_sphinx = '1.0' file_insertion_enabled = True raw_enabled = True # Set up doxylink diff --git a/doc/advanced/content/index.rst b/doc/advanced/content/index.rst index 99716d5a..cb2c4beb 100644 --- a/doc/advanced/content/index.rst +++ b/doc/advanced/content/index.rst @@ -96,7 +96,7 @@ development that everyone should follow: An in-depth discussion about the PCL 2.x API can be found here. -Commiting changes to the git master +Committing changes to the git master ----------------------------------- In order to oversee the commit messages more easier and that the changelist looks homogenous please keep the following format: diff --git a/doc/advanced/content/pcl2.rst b/doc/advanced/content/pcl2.rst index 27a27cd0..1a763989 100644 --- a/doc/advanced/content/pcl2.rst +++ b/doc/advanced/content/pcl2.rst @@ -31,7 +31,7 @@ The 1.x API includes the following data members: * **bool** :pcl:`is_dense ` - true if the data contains only valid numbers (e.g., no NaN or -/+Inf, etc). False otherwise. * **Eigen::Vector4f** :pcl:`sensor_origin_ ` - the origin (pose) of the acquisition sensor in the current data coordinate system. - * **Eigen::Quaternionf** :pcl:`sensor_orientation_ ` - the origin (orientation) of hte acquisition sensor in the current data coordinate system. + * **Eigen::Quaternionf** :pcl:`sensor_orientation_ ` - the origin (orientation) of the acquisition sensor in the current data coordinate system. Proposals for the 2.x API: @@ -75,7 +75,7 @@ Proposals for the 2.x API: #. Eigen::Vector4f or Eigen::Vector3f ?? - #. Large points cause significant perfomance penalty for GPU. Let's assume that point sizes up to 16 bytes are suitable. This is some compromise between SOA and AOS. Structures like pcl::Normal (size = 32) is not desirable. SOA is better in this case. + #. Large points cause significant performance penalty for GPU. Let's assume that point sizes up to 16 bytes are suitable. This is some compromise between SOA and AOS. Structures like pcl::Normal (size = 32) is not desirable. SOA is better in this case. 1.3 GPU support diff --git a/doc/doxygen/CMakeLists.txt b/doc/doxygen/CMakeLists.txt index 7f90495d..4cd7b6ab 100644 --- a/doc/doxygen/CMakeLists.txt +++ b/doc/doxygen/CMakeLists.txt @@ -1,5 +1,5 @@ if(WITH_DOCS) - find_package(Doxygen) + find_package(Doxygen REQUIRED) if(DOXYGEN_FOUND) find_package(HTMLHelp) endif(DOXYGEN_FOUND) diff --git a/doc/doxygen/doxyfile.in b/doc/doxygen/doxyfile.in index 88b36d57..e2047862 100644 --- a/doc/doxygen/doxyfile.in +++ b/doc/doxygen/doxyfile.in @@ -132,7 +132,7 @@ EXCLUDE_SYMBOLS = EXAMPLE_PATH = EXAMPLE_PATTERNS = * EXAMPLE_RECURSIVE = NO -IMAGE_PATH = +IMAGE_PATH = "@PCL_SOURCE_DIR@/doc/doxygen" INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO @@ -205,7 +205,7 @@ USE_MATHJAX = NO MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest MATHJAX_EXTENSIONS = SEARCHENGINE = @DOCUMENTATION_SEARCHENGINE@ -SERVER_BASED_SEARCH = YES +SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output diff --git a/doc/tutorials/content/adding_custom_ptype.rst b/doc/tutorials/content/adding_custom_ptype.rst index 51914fea..3879c193 100644 --- a/doc/tutorials/content/adding_custom_ptype.rst +++ b/doc/tutorials/content/adding_custom_ptype.rst @@ -127,7 +127,7 @@ addition, the type that you want, might already be defined for you. * `PointXYZRGBA` - Members: float x, y, z; uint32_t rgba; - Similar to `PointXYZI`, except `rgba` containts the RGBA information packed + Similar to `PointXYZI`, except `rgba` contains the RGBA information packed into a single integer. .. code-block:: cpp @@ -197,7 +197,7 @@ addition, the type that you want, might already be defined for you. * `InterestPoint` - float x, y, z, strength; - Similar to `PointXYZI`, except `strength` containts a measure of the strength + Similar to `PointXYZI`, except `strength` contains a measure of the strength of the keypoint. .. code-block:: cpp @@ -374,8 +374,8 @@ addition, the type that you want, might already be defined for you. * `PointWithRange` - float x, y, z (union with float point[4]), range; - Similar to `PointXYZI`, except `range` containts a measure of the distance - from the acqusition viewpoint to the point in the world. + Similar to `PointXYZI`, except `range` contains a measure of the distance + from the acquisition viewpoint to the point in the world. .. code-block:: cpp @@ -401,7 +401,7 @@ addition, the type that you want, might already be defined for you. * `PointWithViewpoint` - float x, y, z, vp_x, vp_y, vp_z; - Similar to `PointXYZI`, except `vp_x`, `vp_y`, and `vp_z` containt the + Similar to `PointXYZI`, except `vp_x`, `vp_y`, and `vp_z` contain the acquisition viewpoint as a 3D point. .. code-block:: cpp @@ -584,7 +584,7 @@ addition, the type that you want, might already be defined for you. * `PointWithScale` - float x, y, z, scale; - Similar to `PointXYZI`, except `scale` containts the scale at which a certain + Similar to `PointXYZI`, except `scale` contains the scale at which a certain point was considered for a geometric operation (e.g. the radius of the sphere for its nearest neighbors computation, the window size, etc). diff --git a/doc/tutorials/content/basic_structures.rst b/doc/tutorials/content/basic_structures.rst index ea5d7f82..45ce6e02 100644 --- a/doc/tutorials/content/basic_structures.rst +++ b/doc/tutorials/content/basic_structures.rst @@ -20,7 +20,7 @@ PointCloud is a C++ class which contains the following data fields: that resemble an organized image (or matrix) like structure, where the data is split into rows and columns. Examples of such point clouds include data coming from stereo cameras or Time Of Flight cameras. The - advantages of a organized dataset is that by knowing the relationship + advantages of an organized dataset is that by knowing the relationship between adjacent points (e.g. pixels), nearest neighbor operations are much more efficient, thus speeding up the computation and lowering the costs of certain algorithms in PCL. @@ -45,7 +45,7 @@ PointCloud is a C++ class which contains the following data fields: Example:: - cloud.width = 640; // Image-like organized structure, with 640 rows and 480 columns, + cloud.width = 640; // Image-like organized structure, with 480 rows and 640 columns, cloud.height = 480; // thus 640*480=307200 points total in the dataset Example:: diff --git a/doc/tutorials/content/benchmark.rst b/doc/tutorials/content/benchmark.rst index 52a76b57..5cd75abc 100644 --- a/doc/tutorials/content/benchmark.rst +++ b/doc/tutorials/content/benchmark.rst @@ -4,7 +4,7 @@ Benchmarking 3D --------------- This document introduces benchmarking concepts for 3D algorithms. By -*benchmarking* here we refer to the posibility of testing different +*benchmarking* here we refer to the possibility of testing different computational pipelines in an **easy manner**. The goal is to test their reproductibility with respect to a particular problem of general interest. @@ -50,7 +50,7 @@ A higher level representation as mentioned before will be herein represented by * a combination of the above. -In addtion, feature descriptors can be: +In addition, feature descriptors can be: * **local** - estimated only at a set of discrete keypoints, using the information from neighboring pixels/points; * **global**, or meta-local - estimated on entire objects or the entire input dataset. diff --git a/doc/tutorials/content/benchmark_filters.rst b/doc/tutorials/content/benchmark_filters.rst index 9ad99215..10cbafc1 100644 --- a/doc/tutorials/content/benchmark_filters.rst +++ b/doc/tutorials/content/benchmark_filters.rst @@ -5,7 +5,7 @@ Filter Benchmarking This document introduces benchmarking concepts for filtering algorithms. By *benchmarking* here we refer to the possibility of testing different -parameters for each filter algorithm on a specific point cloud in an **easy manner**. The goal is to find the best paramaters of a certain filter that best describe the original point cloud without removing useful data. +parameters for each filter algorithm on a specific point cloud in an **easy manner**. The goal is to find the best parameters of a certain filter that best describe the original point cloud without removing useful data. Benchmarking Filter Algorithms ------------------------------- diff --git a/doc/tutorials/content/building_pcl.rst b/doc/tutorials/content/building_pcl.rst index f0b8fafc..84f7de7e 100644 --- a/doc/tutorials/content/building_pcl.rst +++ b/doc/tutorials/content/building_pcl.rst @@ -116,11 +116,11 @@ YYY then XXX will be built but won't appear in the cache. You can also change the build type: -* **Debug**: means that no optimization is done and all the debugging symbols are imbedded into the libraries file. This is plateform and compiler dependent. On Linux with gcc this is equivalent to running gcc with `-O0 -g -ggdb -Wall` +* **Debug**: means that no optimization is done and all the debugging symbols are embedded into the libraries file. This is platform and compiler dependent. On Linux with gcc this is equivalent to running gcc with `-O0 -g -ggdb -Wall` -* **Release**: the compiled code is optimized and no debug information will be print out. This will lead to `-O3` for gcc and `-O5` for clang +* **Release**: the compiled code is optimized and no debug information will be printed out. This will lead to `-O3` for gcc and `-O5` for clang -* **RelWithDebInfo**: the compiled code is optimized but debugging data is also imbedded in the libraries. This is a tradeoff between the two former ones. +* **RelWithDebInfo**: the compiled code is optimized but debugging data is also embedded in the libraries. This is a tradeoff between the two former ones. * **MinSizeRel**: this, normally, results in the smallest libraries you can build. This is interesting when building for Android or a restricted memory/space system. @@ -134,7 +134,7 @@ Tweaking advanced settings Now we are done with all the basic stuff. To turn on advanced cache options hit `t` while in ccmake. Advanced options become especially useful when you have dependencies -installed in unusal locations and thus cmake hangs with +installed in unusual locations and thus cmake hangs with `XXX_NOT_FOUND` this can even prevent you from building PCL although you have all the dependencies installed. In this section we will discuss each dependency entry so that you can configure/build or @@ -183,7 +183,7 @@ message you get from CMake, you can check or edit each dependency specific variables and give it the value that best fits your needs. UNIX users generally don't have to bother with debug vs release versions -they are fully complient. You would just loose debug symbols if you use +they are fully compliant. You would just loose debug symbols if you use release libraries version instead of debug while you will end up with much more verbose output and slower execution. This said, Windows MSVC users and Apple iCode ones can build debug/release from the same project, thus diff --git a/doc/tutorials/content/compiling_pcl_dependencies_windows.rst b/doc/tutorials/content/compiling_pcl_dependencies_windows.rst index f8c4dace..d2fe8db0 100644 --- a/doc/tutorials/content/compiling_pcl_dependencies_windows.rst +++ b/doc/tutorials/content/compiling_pcl_dependencies_windows.rst @@ -95,7 +95,7 @@ like:: Where to build binaries: C:/PCL_dependencies/boost-cmake/build Before clicking on "Configure", click on "Add Entry" button in the top right of CMake gui, in - the popup window, fill the fiels as follows:: + the popup window, fill the fields as follows:: Name : LIBPREFIX Type : STRING @@ -151,7 +151,7 @@ like:: +-- optional python bindings disabled since PYTHON_FOUND is false. + tr1 - Now, click "Generate". A Visual Studio solution file will be genrated inside the build folder + Now, click "Generate". A Visual Studio solution file will be generated inside the build folder (e.g. C:/PCL_dependencies/boost-cmake/build). Open the `Boost.sln` file, then right click on `INSTALL` project and choose `Build`. The `INSTALL`project will trigger the build of all the projects in the solution file, and then will install the build libraries along with the header files to the default @@ -177,7 +177,7 @@ like:: Where to build binaries: C:/PCL_dependencies/flann-1.7.1-src/build Hit the "Configure" button. Proceed and be sure to choose the correct "Generator" on the next window. - You can safley ignore any warning message about hdf5. + You can safely ignore any warning message about hdf5. Now, on my machine I had to manually set the `BUILD_PYTHON_BINDINGS` and `BUILD_MATLAB_BINDINGS` to OFF otherwise it would not continue to the next @@ -210,7 +210,7 @@ like:: Where to build binaries: C:/PCL_dependencies/qhull-2011.1/build Before clicking on "Configure", click on "Add Entry" button in the top right of CMake gui, in - the popup window, fill the fiels as follows:: + the popup window, fill the fields as follows:: Name : CMAKE_DEBUG_POSTFIX Type : STRING @@ -282,7 +282,7 @@ like:: - **GTest** : - In case you want PCL tests (not recommanded for users), you need to compile the `googletest` library (GTest). + In case you want PCL tests (not recommended for users), you need to compile the `googletest` library (GTest). Setup the CMake fields as usual:: Where is my source code: C:/PCL_dependencies/gtest-1.6.0 diff --git a/doc/tutorials/content/compiling_pcl_posix.rst b/doc/tutorials/content/compiling_pcl_posix.rst index 43ef9057..fd1dfaaf 100644 --- a/doc/tutorials/content/compiling_pcl_posix.rst +++ b/doc/tutorials/content/compiling_pcl_posix.rst @@ -46,7 +46,7 @@ And install the result:: make -j2 install -Or alternatively, if you did not change the variable which declares where PCL should be installed, do:: +Or alternatively, if you did not change the variable which declares where PCL should be installed, do:: sudo make -j2 install diff --git a/doc/tutorials/content/compiling_pcl_windows.rst b/doc/tutorials/content/compiling_pcl_windows.rst index f3d7dcc4..b1f54160 100644 --- a/doc/tutorials/content/compiling_pcl_windows.rst +++ b/doc/tutorials/content/compiling_pcl_windows.rst @@ -109,7 +109,7 @@ Now hit the "Configure" button. You will be asked for a `generator`. A generator "**Visual Studio 10**" generator. If you want to build 64bit PCL, then pick the "**Visual Studio 10 Win64**". Make sure you have installed the right third party dependencies. You cannot mix 32bit and 64bit code, and it is - highly recommanded to not mix codes compiled with different compilers. + highly recommended to not mix codes compiled with different compilers. .. image:: images/windows/cmake_generator.png :alt: Choosing a generator @@ -146,7 +146,7 @@ Let's check whether CMake did actually find the needed third party dependencies :alt: Boost :align: center - Let's tell CMake where boost headers are by specifiying the headers path in **Boost_INCLUDE_DIR** variable. For example, my boost + Let's tell CMake where boost headers are by specifying the headers path in **Boost_INCLUDE_DIR** variable. For example, my boost headers are in C:\\Program Files\\PCL-Boost\\include (C:\\Program Files\\Boost\\include for newer installers). Then, let's hit `configure` again ! Hopefully, CMake is now able to find all the other items (the libraries). @@ -162,7 +162,7 @@ Let's check whether CMake did actually find the needed third party dependencies - **Eigen** : - Eigen is a header-only library, thus, we need only **EIGEN_INCLUDE_DIR** to be set. Hopefully, CMake did fing Eigen. + Eigen is a header-only library, thus, we need only **EIGEN_INCLUDE_DIR** to be set. Hopefully, CMake did find Eigen. .. image:: images/windows/cmake_eigen_include_dir.png :alt: Eigen include dir @@ -245,7 +245,7 @@ Once CMake has found all the needed dependencies, let's see the PCL specific CMa :alt: PCL :align: center -- **PCL_SHARED_LIBS** is checked by default. Uncheck it if you want static PCL libs (not recommanded). +- **PCL_SHARED_LIBS** is checked by default. Uncheck it if you want static PCL libs (not recommended). - **CMAKE_INSTALL_PREFIX** is where PCL will be installed after building it (more information on this later). @@ -278,7 +278,7 @@ Building the "ALL_BUILD" project will build everything. Installing PCL -------------- -To install the built libraries and executbles, you need to build the "INSTALL" project in the solution explorer. +To install the built libraries and executables, you need to build the "INSTALL" project in the solution explorer. This utility project will copy PCL headers, libraries and executable to the directory defined by the **CMAKE_INSTALL_PREFIX** CMake variable. @@ -291,7 +291,7 @@ CMake variable. .. note:: - It is highly recommanded to add the bin folder in PCL installation tree (e.g. C:\\Program Files\\PCL\\bin) + It is highly recommended to add the bin folder in PCL installation tree (e.g. C:\\Program Files\\PCL\\bin) to your **PATH** environment variable. Advanced topics diff --git a/doc/tutorials/content/compression.rst b/doc/tutorials/content/compression.rst index 5bb5ada1..f3600e56 100644 --- a/doc/tutorials/content/compression.rst +++ b/doc/tutorials/content/compression.rst @@ -144,25 +144,25 @@ The following compression profiles are available: - **LOW_RES_ONLINE_COMPRESSION_WITH_COLOR** 1 cubic centimeter resolution, color, fast online encoding - - **MED_RES_ONLINE_COMPRESSION_WITHOUT_COLOR** 5 cubic milimeter resolution, no color, fast online encoding + - **MED_RES_ONLINE_COMPRESSION_WITHOUT_COLOR** 5 cubic millimeter resolution, no color, fast online encoding - - **MED_RES_ONLINE_COMPRESSION_WITH_COLOR** 5 cubic milimeter resolution, color, fast online encoding + - **MED_RES_ONLINE_COMPRESSION_WITH_COLOR** 5 cubic millimeter resolution, color, fast online encoding - - **HIGH_RES_ONLINE_COMPRESSION_WITHOUT_COLOR** 1 cubic milimeter resolution, no color, fast online encoding + - **HIGH_RES_ONLINE_COMPRESSION_WITHOUT_COLOR** 1 cubic millimeter resolution, no color, fast online encoding - - **HIGH_RES_ONLINE_COMPRESSION_WITH_COLOR** 1 cubic milimeter resolution, color, fast online encoding + - **HIGH_RES_ONLINE_COMPRESSION_WITH_COLOR** 1 cubic millimeter resolution, color, fast online encoding - **LOW_RES_OFFLINE_COMPRESSION_WITHOUT_COLOR** 1 cubic centimeter resolution, no color, efficient offline encoding - **LOW_RES_OFFLINE_COMPRESSION_WITH_COLOR** 1 cubic centimeter resolution, color, efficient offline encoding - - **MED_RES_OFFLINE_COMPRESSION_WITHOUT_COLOR** 5 cubic milimeter resolution, no color, efficient offline encoding + - **MED_RES_OFFLINE_COMPRESSION_WITHOUT_COLOR** 5 cubic millimeter resolution, no color, efficient offline encoding - - **MED_RES_OFFLINE_COMPRESSION_WITH_COLOR** 5 cubic milimeter resolution, color, efficient offline encoding + - **MED_RES_OFFLINE_COMPRESSION_WITH_COLOR** 5 cubic millimeter resolution, color, efficient offline encoding - - **HIGH_RES_OFFLINE_COMPRESSION_WITHOUT_COLOR** 1 cubic milimeter resolution, no color, efficient offline encoding + - **HIGH_RES_OFFLINE_COMPRESSION_WITHOUT_COLOR** 1 cubic millimeter resolution, no color, efficient offline encoding - - **HIGH_RES_OFFLINE_COMPRESSION_WITH_COLOR** 1 cubic milimeter resolution, color, efficient offline encoding + - **HIGH_RES_OFFLINE_COMPRESSION_WITH_COLOR** 1 cubic millimeter resolution, color, efficient offline encoding - **MANUAL_CONFIGURATION** enables manual configuration for advanced parametrization @@ -198,9 +198,9 @@ The advanced parametrization is explained in the following: decreased compression performance. This enables a trade-off between high frame/update rates and compression efficiency. - **doVoxelGridDownDownSampling_arg**: If activated, only the hierarchical octree data structure is encoded. The decoder generated points at the voxel centers. In this - way, the point cloud becomes downsampled during compression while archieving high compression performance. + way, the point cloud becomes downsampled during compression while achieving high compression performance. - - **iFrameRate_arg**: The point cloud compression scheme differentially encodes point clouds. In this way, differences between the incoming point cloud and the previously encoded pointcloud is encoded in order to archive maximum compression performance. The iFrameRate_arg allows to specify the rate of frames in the stream at which incoming point clouds are **not** differentially encoded (similar to I/P-frames in video coding). + - **iFrameRate_arg**: The point cloud compression scheme differentially encodes point clouds. In this way, differences between the incoming point cloud and the previously encoded pointcloud is encoded in order to achieve maximum compression performance. The iFrameRate_arg allows to specify the rate of frames in the stream at which incoming point clouds are **not** differentially encoded (similar to I/P-frames in video coding). - **doColorEncoding_arg**: This option enables color component encoding. diff --git a/doc/tutorials/content/concatenate_clouds.rst b/doc/tutorials/content/concatenate_clouds.rst index 8f90958e..68b3f705 100644 --- a/doc/tutorials/content/concatenate_clouds.rst +++ b/doc/tutorials/content/concatenate_clouds.rst @@ -3,9 +3,9 @@ Concatenate the points of two Point Clouds ------------------------------------------ -In this tutorial we will learn how to concatenating the points of two different +In this tutorial we will learn how to concatenate the points of two different point clouds. The constraint imposed here is that the type and number of fields -in the two datasets has to be equal. We will also learn how to concatenate the fields (e.g., +in the two datasets have to be equal. We will also learn how to concatenate the fields (e.g., dimensions) of two different point clouds. The constraint imposed here is that the number of points in the two datasets has to be equal. diff --git a/doc/tutorials/content/concatenate_points.rst b/doc/tutorials/content/concatenate_points.rst index 7a01b82c..b86b169e 100644 --- a/doc/tutorials/content/concatenate_points.rst +++ b/doc/tutorials/content/concatenate_points.rst @@ -5,7 +5,7 @@ Concatenate the points or the fields of two Point Clouds In this tutorial we will learn how to concatenating the points of two different point clouds. The constraint imposed here is that the type and number of fields -in the two datasets has to be equal. +in the two datasets have to be equal. The code -------- diff --git a/doc/tutorials/content/conditional_euclidean_clustering.rst b/doc/tutorials/content/conditional_euclidean_clustering.rst index acf1f4e9..ee207734 100644 --- a/doc/tutorials/content/conditional_euclidean_clustering.rst +++ b/doc/tutorials/content/conditional_euclidean_clustering.rst @@ -26,7 +26,7 @@ The clusters classified as too small or too large can still be retrieved afterwa The Code -------- -First, download the dataset `Statues_4.pcd `_ and save it somewhere to disk. +First, download the dataset `Statues_4.pcd `_ and extract the PCD file from the archive. This is a very large data set of an outdoor environment where we aim to cluster the separate objects and also want to separate the building from the ground plane even though it is attached in a Euclidean sense. Now create a file, let's say, ``conditional_euclidean_clustering.cpp`` in your favorite editor, and place the following inside it: @@ -95,7 +95,7 @@ Lines 97-109 contain a piece of code that is a quick and dirty fix to visualize :language: cpp :lines: 97-109 -When the output point cloud is opened with PCL's standard PCD viewer, pressing '5' will switch to the intenisty channel visualization. +When the output point cloud is opened with PCL's standard PCD viewer, pressing '5' will switch to the intensity channel visualization. The too-small clusters will be colored red, the too-large clusters will be colored blue, and the actual clusters/objects of interest will be colored randomly in between yellow and cyan hues. Compiling and running the program diff --git a/doc/tutorials/content/conditional_removal.rst b/doc/tutorials/content/conditional_removal.rst index 2154fe45..3859ad48 100644 --- a/doc/tutorials/content/conditional_removal.rst +++ b/doc/tutorials/content/conditional_removal.rst @@ -3,7 +3,7 @@ Removing outliers using a ConditionalRemoval filter --------------------------------------------------- -This document demonstrates how to use the ConditionalRemoval filter to remove points from a PointCloud that do no satisfy a specific or multiple conditions. +This document demonstrates how to use the ConditionalRemoval filter to remove points from a PointCloud that do not satisfy a specific or multiple conditions. The code -------- @@ -25,13 +25,13 @@ In the following Lines, we define the PointCloud structures, fill in the input c :language: cpp :lines: 8-27 -Then, we create the condition which a given point must satisfy so that it remains in our PointCloud. To do this we must add two comparisons to the condition, greater than 0.0, and less than 0.8. This condition is then used to build the filter. +Then, we create the condition which a given point must satisfy so that it remains in our PointCloud. To do this we must add two comparisons to the condition, greater than 0.0, and less than 0.8. This condition is then used to build the filter. .. literalinclude:: sources/conditional_removal/conditional_removal.cpp :language: cpp :lines: 28-39 -This last bit of code just applies the filter to our original PointCloud, and removes all of the points that do not satisfy the conditions we specified. Then it outputs all of the points remaining in the PointCloud. +This last bit of code just applies the filter to our original PointCloud, and removes all of the points that do not satisfy the conditions we specified. Then it outputs all of the points remaining in the PointCloud. .. literalinclude:: sources/conditional_removal/conditional_removal.cpp :language: cpp diff --git a/doc/tutorials/content/depth_sense_grabber.rst b/doc/tutorials/content/depth_sense_grabber.rst index 48a9a05e..f03456e7 100644 --- a/doc/tutorials/content/depth_sense_grabber.rst +++ b/doc/tutorials/content/depth_sense_grabber.rst @@ -76,7 +76,7 @@ class that implements the standard PCL grabber interface. You can run the tool with `--help` option to view the usage guide. -The video below demontrates the features of the DepthSense viewer tool. Please +The video below demonstrates the features of the DepthSense viewer tool. Please note that the bilateral filtering (which can be observed in the end of the video) is currently disabled is the tool. diff --git a/doc/tutorials/content/don_segmentation.rst b/doc/tutorials/content/don_segmentation.rst index 4d5b0569..5dfd8c31 100644 --- a/doc/tutorials/content/don_segmentation.rst +++ b/doc/tutorials/content/don_segmentation.rst @@ -36,7 +36,7 @@ where :math:`$r_s, r_l \in \mathbb{R}$`, :math:`$r_s`_. +.. For this tutorial we will use publicly available (creative commons licensed) urban LiDAR data from the [KITTI]_ project. This data is collected from a Velodyne LiDAR scanner mounted on a car, for the purpose of evaluating self-driving cars. To convert the data set to PCL compatible point clouds please see [KITTIPCL]_. An example scan is presented here from the KITTI data set in PCL form, is available for use with this example, ``_. The Code ======== @@ -116,7 +116,7 @@ This is perhaps the most important section of code, estimating the normals. This :language: cpp :lines: 90-102 -Next we calculate the normals using our normal estimation class for both the large and small radius. It is important to use the ``NormalEstimation.setRadiusSearch()`` method v.s. the ``NormalEstimation.setMaximumNeighbours()`` method or equivilant. If the normal estimate is restricted to a set number of neighbours, it may not be based on the complete surface of the given radius, and thus is not suitable for the Difference of Normals features. +Next we calculate the normals using our normal estimation class for both the large and small radius. It is important to use the ``NormalEstimation.setRadiusSearch()`` method v.s. the ``NormalEstimation.setMaximumNeighbours()`` method or equivalent. If the normal estimate is restricted to a set number of neighbours, it may not be based on the complete surface of the given radius, and thus is not suitable for the Difference of Normals features. .. note:: For large supporting radii in dense point clouds, calculating the normal would be a very computationally intensive task potentially utilizing thousands of points in the calculation, when hundreds are more than enough for an accurate estimate. A simple method to speed up the calculation is to uniformly subsample the pointcloud when doing a large radius search, see the full example code in the PCL distribution at ``examples/features/example_difference_of_normals.cpp`` for more details. diff --git a/doc/tutorials/content/ensenso_cameras.rst b/doc/tutorials/content/ensenso_cameras.rst index 29b96529..c993b787 100644 --- a/doc/tutorials/content/ensenso_cameras.rst +++ b/doc/tutorials/content/ensenso_cameras.rst @@ -113,7 +113,7 @@ Extrinsic calibration If you want to perform extrinsic calibration of the sensor, please first make sure your EnsensoSDK version is greater than 1.3. A fully automated extrinsic calibration ROS package is available to help you calibrate the sensor mounted on a robot arm, -the package can be found in the `Institut Maupertuis repository `_. +the package can be found in the `Institut Maupertuis repository `_. The following video shows the automatic calibration procedure on a Fanuc R1000iA 80f industrial robot: diff --git a/doc/tutorials/content/extract_indices.rst b/doc/tutorials/content/extract_indices.rst index 5574d529..4026601b 100644 --- a/doc/tutorials/content/extract_indices.rst +++ b/doc/tutorials/content/extract_indices.rst @@ -44,7 +44,7 @@ needed to spend within the segmentation loop. The next block of code deals with the parametric segmentation. To keep the -tutorial simple, its its explanation will be skipped for now. Please see the +tutorial simple, its explanation will be skipped for now. Please see the **segmentation** tutorials (in particular :ref:`planar_segmentation`) for more information. diff --git a/doc/tutorials/content/gasd_estimation.rst b/doc/tutorials/content/gasd_estimation.rst new file mode 100644 index 00000000..0a1ccd83 --- /dev/null +++ b/doc/tutorials/content/gasd_estimation.rst @@ -0,0 +1,153 @@ +.. _gasd_estimation: + +Globally Aligned Spatial Distribution (GASD) descriptors +-------------------------------------------------------- + +This document describes the Globally Aligned Spatial Distribution ([GASD]_) global descriptor to be used for efficient object recognition and pose estimation. + +GASD is based on the estimation of a reference frame for the whole point cloud that represents an object instance, which is used for aligning it with the canonical coordinate system. After that, a descriptor is computed for the aligned point cloud based on how its 3D points are spatially distributed. Such descriptor may also be extended with color distribution throughout the aligned point cloud. The global alignment transforms of matched point clouds are used for computing object pose. For more information please see [GASD]_. + +Theoretical primer +------------------ + +The Globally Aligned Spatial Distribution (or GASD) global description method takes as input a 3D point cloud that represents a partial view of a given object. The first step consists in estimating a reference frame for the point cloud, which allows the computation of a transform that aligns it to the canonical coordinate system, making the descriptor pose invariant. After alignment, a shape descriptor is computed for the point cloud based on the spatial distribution of the 3D points. Color distribution along the point cloud can also be taken into account for obtaining a shape and color descriptor with a higher discriminative power. Object recognition is then performed by matching query and train descriptors of partial views. The pose of each recognized object is also computed from the alignment transforms of matched query and train partial views. + +The reference frame is estimated using a Principal Component Analysis (PCA) approach. Given a set of 3D points :math:`\boldsymbol{P_i}` that represents a partial view of an object, with :math:`i\in\{1, ..., n\}`, the first step consists in computing their centroid :math:`\boldsymbol{\overline{P}}`, which is the origin of the reference frame. Then a covariance matrix :math:`\boldsymbol{C}` is computed from :math:`\boldsymbol{P_i}` and :math:`\boldsymbol{\overline{P}}` as follows: + +.. math:: + + \boldsymbol{C}=\frac{1}{n}\sum_{i=1}^{n}(\boldsymbol{P_i}-\boldsymbol{\overline{P}})(\boldsymbol{P_i}-\boldsymbol{\overline{P}})^T. + +After that, the eigenvalues :math:`\lambda_j` and corresponding eigenvectors :math:`\boldsymbol{v_j}` of :math:`\boldsymbol{C}` are obtained, with :math:`j\in\{1, 2, 3\}`, such that :math:`\boldsymbol{C}\boldsymbol{v_j}=\lambda_j\boldsymbol{v_j}`. Considering that the eigenvalues are arranged in ascending order, the eigenvector :math:`\boldsymbol{v_1}` associated with the minimal eigenvalue is used as the :math:`z` axis of the reference frame. If the angle between :math:`\boldsymbol{v_1}` and the viewing direction is in the :math:`[-90^{\circ}, 90^{\circ}]` range, then :math:`\boldsymbol{v_1}` is negated. This ensures that the :math:`z` axis always points towards the viewer. The :math:`x` axis of the reference frame is the eigenvector :math:`\boldsymbol{v_3}` associated with the maximal eigenvalue. The :math:`y` axis is given by :math:`\boldsymbol{v_2}=\boldsymbol{v_1}\times\boldsymbol{v_3}`. + +From the reference frame, it is possible to compute a transform :math:`[\boldsymbol{R} | \boldsymbol{t}]` that aligns it with the canonical coordinate system. All the points :math:`\boldsymbol{P_i}` of the partial view are then transformed with :math:`[\boldsymbol{R} | \boldsymbol{t}]`, which is defined as follows: + +.. math:: + + \begin{bmatrix} + \boldsymbol{R} & \boldsymbol{t} \\ + \boldsymbol{0} & 1 + \end{bmatrix}= + \begin{bmatrix} + \boldsymbol{v_3}^T & -\boldsymbol{v_3}^T\boldsymbol{\overline{P}} \\ + \boldsymbol{v_2}^T & -\boldsymbol{v_2}^T\boldsymbol{\overline{P}} \\ + \boldsymbol{v_1}^T & -\boldsymbol{v_1}^T\boldsymbol{\overline{P}} \\ + \boldsymbol{0} & 1 + \end{bmatrix}. + +Once the point cloud is aligned using the reference frame, a pose invariant global shape descriptor can be computed from it. The point cloud axis-aligned bounding cube centered on the origin is divided into an :math:`m_s \times m_s \times m_s` regular grid. For each grid cell, a histogram with :math:`l_s` bins is computed. If :math:`l_s=1`, then each histogram bin will store the number of points that belong to its correspondent cell in the 3D regular grid. If :math:`l_s>1`, then for each cell it will be computed a histogram of normalized distances between each sample and the cloud centroid. + +The contribution of each sample to the histogram is normalized with respect to the total number of points in the cloud. Optionally, interpolation may be used to distribute the value of each sample into adjacent cells, in an attempt to avoid boundary effects that may cause abrupt changes to the histogram when a sample shifts from being within one cell to another. The descriptor is then obtained by concatenating the computed histograms. + +.. image:: images/gasd_estimation/grid.png + :width: 24% +.. image:: images/gasd_estimation/grid_top_side_bottom_view.png + :width: 72% + +Color information can also be incorporated to the descriptor in order to increase its discriminative power. The color component of the descriptor is computed with an :math:`m_c \times m_c \times m_c` grid similar to the one used for the shape component, but a color histogram is generated for each cell based on the colors of the points that belong to it. Point cloud color is represented in the HSV space and the hue values are accumulated in histograms with :math:`l_c` bins. Similarly to shape component computation, normalization with respect to number of points is performed. Additionally, interpolation of histograms samples may also be performed. The shape and color components are concatenated, resulting in the final descriptor. + +Query and train descriptors are matched using a nearest neighbor search approach. After that, for each matched object instance, a coarse pose is computed using the alignment transforms obtained from the reference frames of the respective query and train partial views. Given the transforms :math:`[\mathbf{R_{q}} | \mathbf{t_{q}}]` and :math:`[\mathbf{R_{t}} | \mathbf{t_{t}}]` that align the query and train partial views, respectively, the object coarse pose :math:`[\mathbf{R_{c}} | \mathbf{t_{c}}]` is obtained by + +.. math:: + + \begin{bmatrix} + \mathbf{R_{c}} & \mathbf{t_{c}} \\ + \mathbf{0} & 1 + \end{bmatrix}= + {\begin{bmatrix} + \mathbf{R_{q}} & \mathbf{t_{q}} \\ + \mathbf{0} & 1 + \end{bmatrix}}^{-1} + \begin{bmatrix} + \mathbf{R_{t}} & \mathbf{t_{t}} \\ + \mathbf{0} & 1 + \end{bmatrix}. + +The coarse pose :math:`[\mathbf{R_{c}} | \mathbf{t_{c}}]` can then be refined using the Iterative Closest Point (ICP) algorithm. + +Estimating GASD features +------------------------ + +The Globally Aligned Spatial Distribution is implemented in PCL as part of the +`pcl_features `_ +library. + +The default values for color GASD parameters are: :math:`m_s=6` (half size of 3), :math:`l_s=1`, :math:`m_c=4` (half size of 2) and :math:`l_c=12` and no histogram interpolation (INTERP_NONE). This results in an array of 984 float values. These are stored in a **pcl::GASDSignature984** point type. The default values for shape only GASD parameters are: :math:`m_s=8` (half size of 4), :math:`l_s=1` and trilinear histogram interpolation (INTERP_TRILINEAR). This results in an array of 512 float values, which may be stored in a **pcl::GASDSignature512** point type. It is also possible to use quadrilinear histogram interpolation (INTERP_QUADRILINEAR). + +The following code snippet will estimate a GASD shape + color descriptor for an input colored point cloud. + +.. code-block:: cpp + :linenos: + + #include + #include + + { + pcl::PointCloud::Ptr cloud (new pcl::PointCloud); + + ... read, pass in or create a point cloud ... + + // Create the GASD estimation class, and pass the input dataset to it + pcl::GASDColorEstimation gasd; + gasd.setInputCloud (cloud); + + // Output datasets + pcl::PointCloud descriptor; + + // Compute the descriptor + gasd.compute (descriptor); + + // Get the alignment transform + Eigen::Matrix4f trans = gasd.getTransform (trans); + + // Unpack histogram bins + for (size_t i = 0; i < size_t( descriptor[0].descriptorSize ()); ++i) + { + descriptor[0].histogram[i]; + } + } + +The following code snippet will estimate a GASD shape only descriptor for an input point cloud. + +.. code-block:: cpp + :linenos: + + #include + #include + + { + pcl::PointCloud::Ptr cloud (new pcl::PointCloud); + + ... read, pass in or create a point cloud ... + + // Create the GASD estimation class, and pass the input dataset to it + pcl::GASDEstimation gasd; + gasd.setInputCloud (cloud); + + // Output datasets + pcl::PointCloud descriptor; + + // Compute the descriptor + gasd.compute (descriptor); + + // Get the alignment transform + Eigen::Matrix4f trans = gasd.getTransform (trans); + + // Unpack histogram bins + for (size_t i = 0; i < size_t( descriptor[0].descriptorSize ()); ++i) + { + descriptor[0].histogram[i]; + } + } + +.. [GASD] http://www.cin.ufpe.br/~jpsml/uploads/8/2/6/7/82675770/pid4349755.pdf +.. note:: + @InProceedings{Lima16SIBGRAPI, + author = {Joao Paulo Lima and Veronica Teichrieb}, + title = {An Efficient Global Point Cloud Descriptor for Object Recognition and Pose Estimation}, + booktitle = {Proceedings of the 29th SIBGRAPI - Conference on Graphics, Patterns and Images}, + year = {2016}, + address = {Sao Jose dos Campos, Brazil}, + month = {October} + } + diff --git a/doc/tutorials/content/gpu_install.rst b/doc/tutorials/content/gpu_install.rst index e8585833..beb3a258 100644 --- a/doc/tutorials/content/gpu_install.rst +++ b/doc/tutorials/content/gpu_install.rst @@ -91,7 +91,7 @@ Go to your PCL root folder and do:: $ ccmake .. Press c to configure ccmake, press t to toggle to the advanced mode as a number of options -only appear in advanced mode. The latest CUDA algorithms are beeing kept in the GPU project, for +only appear in advanced mode. The latest CUDA algorithms are being kept in the GPU project, for this the BUILD_GPU option needs to be on and the BUILD_gpu_ indicate the different GPU subprojects. diff --git a/doc/tutorials/content/gpu_people.rst b/doc/tutorials/content/gpu_people.rst index a14add10..f5ebfbbc 100644 --- a/doc/tutorials/content/gpu_people.rst +++ b/doc/tutorials/content/gpu_people.rst @@ -13,7 +13,7 @@ has been presented on ICRA2012 and IROS2012 and an official reference for a jour This shows how to detect people with an Primesense device, the full version working on oni and pcd files can be found in the git master. -The code assumes a organised and projectable pointcloud, and should work with other +The code assumes an organised and projectable pointcloud, and should work with other sensors then the Primesense device. .. image:: images/gpu/people/ss26_1.jpg diff --git a/doc/tutorials/content/ground_based_rgbd_people_detection.rst b/doc/tutorials/content/ground_based_rgbd_people_detection.rst index 90f094cd..41c33a3d 100644 --- a/doc/tutorials/content/ground_based_rgbd_people_detection.rst +++ b/doc/tutorials/content/ground_based_rgbd_people_detection.rst @@ -109,7 +109,7 @@ In the main loop, new frames are acquired and processed until the application is The ``people_detector`` object receives as input the current cloud and the estimated ground coefficients and computes people clusters properties, which are stored in :pcl:`PersonCluster ` objects. The ground plane coefficients are re-estimated at every frame by using the previous frame estimate as initial condition. -This procedure allows to adapt to small changes which can occurr to the ground plane equation if the camera is slowly moving. +This procedure allows to adapt to small changes which can occur to the ground plane equation if the camera is slowly moving. .. literalinclude:: sources/ground_based_rgbd_people_detection/src/main_ground_based_people_detection.cpp :language: cpp diff --git a/doc/tutorials/content/hdl_grabber.rst b/doc/tutorials/content/hdl_grabber.rst index 2fb9aaa7..06526156 100644 --- a/doc/tutorials/content/hdl_grabber.rst +++ b/doc/tutorials/content/hdl_grabber.rst @@ -6,7 +6,7 @@ The Velodyne High Definition LiDAR (HDL) Grabber The Velodyne HDL is a network-based 3D LiDAR system that produces 360 degree point clouds containing over 700,000 points every second. -The HDL Grabber provided in PCL mimicks other Grabbers, making it *almost* +The HDL Grabber provided in PCL mimics other Grabbers, making it *almost* plug-and-play. Because the HDL devices are network based, however, there are a few gotchas on some platforms. @@ -22,7 +22,7 @@ data for each of the lasers in the device. The HDL-64e consists of The HDL-64e and HDL-32e, by default, produce UDP network packets on the 192.168.3 subnet. Starting with the HDL-32e (Firmware Version 2), -the user can customize this network subnet. +the user can customize this network subnet. The HDL can be connected either directly into your computer, or into a network switch (to include a network switch with a built-in Wireless Access Point). @@ -47,7 +47,7 @@ PCAP Files `Wireshark `_ is a popular Network Packet Analyzer Program which is available for most platforms, including Linux, MacOS and Windows. This tool uses a defacto standard network packet capture file format called `PCAP `_. -Many publically available Velodyne HDL packet captures use this PCAP file format as a means of +Many publicly available Velodyne HDL packet captures use this PCAP file format as a means of recording and playback. These PCAP files can be used with the HDL Grabber if PCL is compiled with PCAP support. @@ -85,7 +85,7 @@ So let's look at the code. The following represents a simplified version of *vis .. code-block:: cpp :linenos: - + #include #include #include @@ -96,7 +96,7 @@ So let's look at the code. The following represents a simplified version of *vis using namespace std; using namespace pcl::console; using namespace pcl::visualization; - + class SimpleHDLViewer { public: @@ -116,7 +116,7 @@ So let's look at the code. The following represents a simplified version of *vis boost::mutex::scoped_lock lock (cloud_mutex_); cloud_ = cloud; } - + void run () { cloud_viewer_->addCoordinateSystem (3.0); @@ -171,7 +171,7 @@ So let's look at the code. The following represents a simplified version of *vis CloudConstPtr cloud_; pcl::visualization::PointCloudColorHandler &handler_; }; - + int main (int argc, char ** argv) { std::string hdlCalibration, pcapFile; @@ -204,10 +204,23 @@ Compiling and running the program Add the following lines to your CMakeLists.txt file: -.. literalinclude:: sources/openni_grabber/CMakeLists.txt - :language: cmake +.. code-block:: cmake :linenos: + cmake_minimum_required(VERSION 2.8 FATAL_ERROR) + + project(pcl_hdl_viewer_simple) + + find_package(PCL 1.2 REQUIRED) + + include_directories(${PCL_INCLUDE_DIRS}) + link_directories(${PCL_LIBRARY_DIRS}) + add_definitions(${PCL_DEFINITIONS}) + + add_executable(pcl_hdl_viewer_simple hdl_viewer_simple.cpp) + target_link_libraries(pcl_hdl_viewer_simple ${PCL_LIBRARIES}) + + _`Disabling Reverse Path Filter` -------------------------------- @@ -218,7 +231,7 @@ is usually the broadcast network IP Address (eg, 255.255.255.255 for a global br x.y.z.255 for a Class C Network [where x.y.z are the first three octets of a Class C network, such as 192.168.1]). -The Source IP Address, on the otherhand, indicates where the packet originated from. Packets +The Source IP Address, on the other hand, indicates where the packet originated from. Packets can be hand-crafted for spoofing-type attacks (eg, pretending to come from somewhere they really didn't). The Reverse Path Filter attempts to detect these instances. The default rule that it uses is that if a packet is received on Network Interface *A*, then if there is no **route** to the **Source IP Address** @@ -267,20 +280,20 @@ returns the following details (some items removed for brevity):: em1: flags=4163 mtu 1500 inet 192.168.128.108 netmask 255.255.255.0 broadcast 192.168.128.255 - + eth0: flags=4163 mtu 1500 inet 192.168.3.1 netmask 255.255.255.0 broadcast 192.168.3.255 Next, let's look at our routing table (again, some items removed for brevity):: $ route -n - + Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 192.168.128.1 0.0.0.0 UG 0 0 0 em1 192.168.3.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 192.168.128.0 0.0.0.0 255.255.255.0 U 0 0 0 em1 - + To add a route to the HDL, assume that the HDL Source IP is 192.168.12.84. You would use the following command:: @@ -289,14 +302,14 @@ following command:: To verify that the route has been added, type the following:: $ route -n - + Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 192.168.128.1 0.0.0.0 UG 0 0 0 em1 192.168.3.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 192.168.12.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 192.168.128.0 0.0.0.0 255.255.255.0 U 0 0 0 em1 - + Now, there is a route back to the source IP address of the HDL on the same interface that the packet came from! diff --git a/doc/tutorials/content/how_features_work.rst b/doc/tutorials/content/how_features_work.rst index 1f89c164..97aac86d 100644 --- a/doc/tutorials/content/how_features_work.rst +++ b/doc/tutorials/content/how_features_work.rst @@ -102,7 +102,7 @@ Because **setInputCloud()** is always required, there are up to four combination * **setIndices() = true, setSearchSurface() = true** - this is probably the rarest case, where both indices and a search surface is given. In this case, features will be estimated for only a subset from the pair, using the search surface information given in **setSearchSurface()**. - Finally, un the figure above, this corresponds to the last (rightmost) case. Here, we assume that q_2's index is not part of the indices vector given for Q, so no neighbors or features will be estimated at q2. + Finally, in the figure above, this corresponds to the last (rightmost) case. Here, we assume that q_2's index is not part of the indices vector given for Q, so no neighbors or features will be estimated at q2. The most useful example when **setSearchSurface()** should be used, is when we have a very dense input dataset, but we do not want to estimate features at all the points in it, but rather at some keypoints discovered using the methods in `pcl_keypoints`, or at a downsampled version of the cloud (e.g., obtained using a `pcl::VoxelGrid` filter). In this case, we pass the downsampled/keypoints input via **setInputCloud()**, and the original data as **setSearchSurface()**. @@ -161,7 +161,7 @@ The following code snippet will estimate a set of surface normals for a subset o // Create a set of indices to be used. For simplicity, we're going to be using the first 10% of the points in cloud std::vector indices (floor (cloud->points.size () / 10)); - for (size_t i = 0; indices.size (); ++i) indices[i] = i; + for (size_t i = 0; i < indices.size (); ++i) indices[i] = i; // Create the normal estimation class, and pass the input dataset to it pcl::NormalEstimation ne; @@ -229,7 +229,7 @@ Finally, the following code snippet will estimate a set of surface normals for a // cloud_normals->points.size () should have the same size as the input cloud_downsampled->points.size () } -.. [RusuDissertation] http://files.rbrusu.com/publications/RusuPhDThesis.pdf +.. [RusuDissertation] http://mediatum.ub.tum.de/doc/800632/941254.pdf .. note:: @PhDThesis{RusuDoctoralDissertation, author = {Radu Bogdan Rusu}, diff --git a/doc/tutorials/content/hull_2d.rst b/doc/tutorials/content/hull_2d.rst index 62fd6fb4..563e1c2d 100644 --- a/doc/tutorials/content/hull_2d.rst +++ b/doc/tutorials/content/hull_2d.rst @@ -35,9 +35,9 @@ The explanation In the following lines of code, a segmentation object is created and some parameters are set. We use the SACMODEL_PLANE to segment this PointCloud, and the method used to find this model is SAC_RANSAC. The actual segmentation -takes place when `seg.segment (*inliers, *coefficents);` is called. This +takes place when `seg.segment (*inliers, *coefficients);` is called. This function stores all of the inlying points (on the plane) to `inliers`, and it -stores the coefficents to the plane `(a * x + b * y + c * z = d)` in +stores the coefficients to the plane `(a * x + b * y + c * z = d)` in `coefficients`. .. literalinclude:: sources/concave_hull_2d/concave_hull_2d.cpp @@ -46,9 +46,9 @@ stores the coefficents to the plane `(a * x + b * y + c * z = d)` in The next bit of code projects the inliers onto the plane model and creates another cloud. One way that we could do this is by just extracting the inliers -that we found before, but in this case we are going to use the coefficents we +that we found before, but in this case we are going to use the coefficients we found before. We set the model type we are looking for and then set the -coefficents, and from that the object knows which points to project from +coefficients, and from that the object knows which points to project from cloud_filtered to cloud_projected. .. literalinclude:: sources/concave_hull_2d/concave_hull_2d.cpp diff --git a/doc/tutorials/content/images/gasd_estimation.png b/doc/tutorials/content/images/gasd_estimation.png new file mode 100644 index 0000000000000000000000000000000000000000..9546c72f44cc7a606c23bb2c7941af0a3e9afd90 GIT binary patch literal 68235 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 Hi?#m&cm)Go literal 0 HcmV?d00001 diff --git a/doc/tutorials/content/images/gasd_estimation/grid.png b/doc/tutorials/content/images/gasd_estimation/grid.png new file mode 100644 index 0000000000000000000000000000000000000000..9546c72f44cc7a606c23bb2c7941af0a3e9afd90 GIT binary patch literal 68235 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 Hi?#m&cm)Go literal 0 HcmV?d00001 diff --git a/doc/tutorials/content/images/gasd_estimation/grid_top_side_bottom_view.png b/doc/tutorials/content/images/gasd_estimation/grid_top_side_bottom_view.png new file mode 100644 index 0000000000000000000000000000000000000000..6719dc4d2afa3f77d62b34a0cbdefd6ed97d2839 GIT binary patch literal 133488 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�A~ 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 literal 0 HcmV?d00001 diff --git a/doc/tutorials/content/index.rst b/doc/tutorials/content/index.rst index 17e210c2..ca487209 100644 --- a/doc/tutorials/content/index.rst +++ b/doc/tutorials/content/index.rst @@ -395,6 +395,21 @@ Features .. |fe_9| image:: images/rops_feature.png :height: 100px + * :ref:`gasd_estimation` + + ======= ====== + |fe_10| Title: **Globally Aligned Spatial Distribution (GASD) descriptors** + + Author: *Joao Paulo Lima* + + Compatibility: >= PCL 1.9 + + This document describes the Globally Aligned Spatial Distribution (GASD) global descriptor to be used for efficient object recognition and pose estimation. + ======= ====== + + .. |fe_10| image:: images/gasd_estimation.png + :height: 100px + .. _filtering_tutorial: Filtering diff --git a/doc/tutorials/content/interactive_icp.rst b/doc/tutorials/content/interactive_icp.rst index f2c41cb5..6c98c207 100644 --- a/doc/tutorials/content/interactive_icp.rst +++ b/doc/tutorials/content/interactive_icp.rst @@ -29,7 +29,7 @@ Subdivide the original mesh to make it more dense : .. image:: images/interactive_icp/add_sub.png :height: 500 -Configure the subdivision to 2 or 3 for example : dont forget to apply the modifier +Configure the subdivision to 2 or 3 for example : don't forget to apply the modifier .. image:: images/interactive_icp/sub2.png :height: 203 @@ -73,7 +73,7 @@ The bool will help us know when the user asks for the next iteration of ICP :language: cpp :lines: 14-24 -This functions takes the reference of a 4x4 matrix and prints the rigid transformation in an human +This function takes the reference of a 4x4 matrix and prints the rigid transformation in an human readable way. .. literalinclude:: sources/interactive_icp/interactive_icp.cpp @@ -182,7 +182,7 @@ and we set max iterations to 1 in lines 90-93. As before we check if ICP as converged, if not we exit the program. **printf("\033[11A");** is a little trick to go up 11 lines in the terminal to write over the last matrix displayed. In short it allows to replace text instead of writing -new lines; making the ouptut more readable. +new lines; making the output more readable. We increment **iterations** to update the text value in the visualizer. Now we want to display the rigid transformation from the original transformed point cloud to @@ -195,7 +195,7 @@ the start to the current iteration. This is basically how it works :: matrix[ICP 0->1]*matrix[ICP 1->2]*matrix[ICP 2->3] = matrix[ICP 0->3] -While this is mathematically true, you will easilly notice that this is not true in this program due to roundings. +While this is mathematically true, you will easily notice that this is not true in this program due to roundings. This is why I introduced the initial ICP iteration parameters. Try to launch the program with 20 initial iterations and save the matrix in a text file. Launch the same program with 1 initial iteration and press space till you go to 20 iterations. You will a notice a slight difference. The matrix with 20 initial iterations is much more accurate than the diff --git a/doc/tutorials/content/iterative_closest_point.rst b/doc/tutorials/content/iterative_closest_point.rst index f58623cf..80782c9b 100644 --- a/doc/tutorials/content/iterative_closest_point.rst +++ b/doc/tutorials/content/iterative_closest_point.rst @@ -62,7 +62,7 @@ performs a simple rigid transform on the pointcloud and again outputs the data v :language: cpp :lines: 37-39 -This creates an instance of an IterativeClosestPoint and gives it some useful information. "icp.setInputCloud(cloud_in);" sets cloud_in as the PointCloud to begin from and "icp.setInputTarget(cloud_out);" sets cloud_out as the PointCloud which we want cloud_in to look like. +This creates an instance of an IterativeClosestPoint and gives it some useful information. "icp.setInputSource(cloud_in);" sets cloud_in as the PointCloud to begin from and "icp.setInputTarget(cloud_out);" sets cloud_out as the PointCloud which we want cloud_in to look like. Next, diff --git a/doc/tutorials/content/matrix_transform.rst b/doc/tutorials/content/matrix_transform.rst index ee546b0d..763f296d 100644 --- a/doc/tutorials/content/matrix_transform.rst +++ b/doc/tutorials/content/matrix_transform.rst @@ -59,7 +59,7 @@ The bool **file_is_pcd** will help us choose between loading PCD or PLY file. :language: cpp :lines: 47-62 -We now load the PCD/PLY file and check if the file was loaded successfuly. Otherwise terminate +We now load the PCD/PLY file and check if the file was loaded successfully. Otherwise terminate the program. @@ -83,7 +83,7 @@ We initialize a 4x4 matrix to identity; :: This means no transformation (no rotation and no translation). We do not use the last row of the matrix. -The first 3 rows and colums (top left) components are the rotation +The first 3 rows and columns (top left) components are the rotation matrix. The first 3 rows of the last column is the translation. .. literalinclude:: sources/matrix_transform/matrix_transform.cpp @@ -104,7 +104,7 @@ This is the transformation we just defined :: :lines: 92-105 This second approach is easier to understand and is less error prone. -Be carefull if you want to apply several rotations; rotations are not commutative ! This means than in most cases: +Be careful if you want to apply several rotations; rotations are not commutative ! This means than in most cases: rotA * rotB != rotB * rotA. .. literalinclude:: sources/matrix_transform/matrix_transform.cpp diff --git a/doc/tutorials/content/min_cut_segmentation.rst b/doc/tutorials/content/min_cut_segmentation.rst index 7453ad8d..f20774c0 100644 --- a/doc/tutorials/content/min_cut_segmentation.rst +++ b/doc/tutorials/content/min_cut_segmentation.rst @@ -39,7 +39,7 @@ The idea of this algorithm is as follows: Radius that occurs in the formula is the input parameter for this algorithm and can be roughly considered as the range from objects center outside of which there are no points that belong to foreground (objects horizontal radius). - #. After all the preparations the search of the minimum cut is made. Based on an analysis of this cut, cloud is divided on forground and + #. After all the preparations the search of the minimum cut is made. Based on an analysis of this cut, cloud is divided on foreground and background points. For more comprehensive information please refer to the article diff --git a/doc/tutorials/content/narf_feature_extraction.rst b/doc/tutorials/content/narf_feature_extraction.rst index be05af66..b334a22a 100644 --- a/doc/tutorials/content/narf_feature_extraction.rst +++ b/doc/tutorials/content/narf_feature_extraction.rst @@ -66,8 +66,8 @@ descriptors in the same place, but for different dominant rotations. The resulting PointCloud contains the type Narf36 (see common/include/pcl/point_types.h) and store the descriptor as a 36 elements float and x,y,z,roll,pitch,yaw to describe the local coordinate frame at which -the feature was extracted. The descriptors can now be compared, e.g., whith the -Manhatten distance (sum of absolute differences). +the feature was extracted. The descriptors can now be compared, e.g., with the +Manhattan distance (sum of absolute differences). The remaining code just visualizes the keypoint positions in a range image widget and also in a 3D viewer. diff --git a/doc/tutorials/content/normal_estimation.rst b/doc/tutorials/content/normal_estimation.rst index ec8dd287..96d572c0 100644 --- a/doc/tutorials/content/normal_estimation.rst +++ b/doc/tutorials/content/normal_estimation.rst @@ -144,7 +144,7 @@ representation. To better illustrate this issue, the figure below presents the effects of selecting a smaller scale (i.e., small **r** or **k**) versus a larger scale (i.e., large **r** or **k**). The left part of the figures depicts a reasonable well chosen scale factor, with estimated surface normals -approximatively perpendicular for the two planar surfaces and small edges +approximately perpendicular for the two planar surfaces and small edges visible all across the table. If the scale factor however is too big (right part), and thus the set of neighbors is larger covering points from adjacent surfaces, the estimated point feature representations get distorted, with diff --git a/doc/tutorials/content/octree.rst b/doc/tutorials/content/octree.rst index 2933b89f..f38adfdc 100644 --- a/doc/tutorials/content/octree.rst +++ b/doc/tutorials/content/octree.rst @@ -30,7 +30,7 @@ We fist define and instantiate a shared PointCloud structure and fill it with ra Then we create an octree instance which is initialized with its resolution. This octree keeps a vector of point indices within its leaf nodes. -The resolution parameter describes the length of the smalles voxels at lowest octree level. The depth of the octree is therefore a function of the resolution as well as +The resolution parameter describes the length of the smallest voxels at lowest octree level. The depth of the octree is therefore a function of the resolution as well as the spatial dimension of the pointcloud. If a bounding box of the pointcloud is know, it should be assigned to the octree by using the defineBoundingBox method. Then we assign a pointer to the PointCloud and add all points to the octree. diff --git a/doc/tutorials/content/openni_grabber.rst b/doc/tutorials/content/openni_grabber.rst index f1a84e6f..71bc33d3 100644 --- a/doc/tutorials/content/openni_grabber.rst +++ b/doc/tutorials/content/openni_grabber.rst @@ -89,7 +89,7 @@ As you can see, the *run ()* function of *SimpleOpenNIViewer* first creates a new *OpenNIGrabber* interface. The next line might seem a bit intimidating at first, but it's not that bad. We create a *boost::bind* object with the address of the callback *cloud_cb_*, we pass a reference to our *SimpleOpenNIViewer* -and the argument palce holder *_1*. +and the argument place holder *_1*. The *bind* then gets casted to a *boost::function* object which is templated on the callback function type, in this case *void (const @@ -171,7 +171,7 @@ Add the following lines to your CMakeLists.txt file: Troubleshooting --------------- -Q: I get an error that theres now device connected: +Q: I get an error that there's no device connected: .. note:: diff --git a/doc/tutorials/content/pcd_file_format.rst b/doc/tutorials/content/pcd_file_format.rst index a7c3982b..210b37be 100644 --- a/doc/tutorials/content/pcd_file_format.rst +++ b/doc/tutorials/content/pcd_file_format.rst @@ -123,7 +123,7 @@ As of version 0.7, the PCD header contains the following entries: Example:: - WIDTH 640 # Image-like organized structure, with 640 rows and 480 columns, + WIDTH 640 # Image-like organized structure, with 480 rows and 640 columns, HEIGHT 480 # thus 640*480=307200 points total in the dataset Example:: diff --git a/doc/tutorials/content/pcl_plotter.rst b/doc/tutorials/content/pcl_plotter.rst index 036b5c1a..bb24b410 100644 --- a/doc/tutorials/content/pcl_plotter.rst +++ b/doc/tutorials/content/pcl_plotter.rst @@ -4,7 +4,7 @@ PCLPlotter ========== PCLPlotter provides a very straightforward and easy interface for plotting graphs. One can visualize all sort of important plots - -from polynomial functions to histograms - inside the library without going to any other softwares (like MATLAB). +from polynomial functions to histograms - inside the library without going to any other software (like MATLAB). Please go through the `documentation `_ when some specific concepts are introduced in this tutorial to know the exact method signatures. The code for the visualization of a plot are usually as simple as the following snippet. diff --git a/doc/tutorials/content/pcl_visualizer.rst b/doc/tutorials/content/pcl_visualizer.rst index 56f279bb..8359fcf7 100644 --- a/doc/tutorials/content/pcl_visualizer.rst +++ b/doc/tutorials/content/pcl_visualizer.rst @@ -439,7 +439,7 @@ window. We must store the view port ID number that is passed back in the fifth parameter and use it in all other calls where we only want to affect that viewport. -We also set the background colour of this viewport, give it a lable +We also set the background colour of this viewport, give it a label based on what we are using the viewport to distinguish, and add our point cloud to it, using an RGB colour handler. diff --git a/doc/tutorials/content/project_inliers.rst b/doc/tutorials/content/project_inliers.rst index 2f02bf41..b570f409 100644 --- a/doc/tutorials/content/project_inliers.rst +++ b/doc/tutorials/content/project_inliers.rst @@ -97,7 +97,7 @@ A graphical display of the projection process is shown below. .. image:: images/project_inliers_2.png -Note that the coordinate axis are represented as red (x), green (y), and blue +Note that the coordinate axes are represented as red (x), green (y), and blue (z). The five points are represented with red as the points before projection and green as the points after projection. Note that their z now lies on the X-Y plane. diff --git a/doc/tutorials/content/qt_visualizer.rst b/doc/tutorials/content/qt_visualizer.rst index 0313ed5f..b59b5449 100644 --- a/doc/tutorials/content/qt_visualizer.rst +++ b/doc/tutorials/content/qt_visualizer.rst @@ -35,7 +35,7 @@ If you want to change this layout you will have to do minor modifications in the Create the folder tree and download the sources files from `github `_. .. note:: - File paths should not contain any special caracter or the compilation might fail with a ``moc: Cannot open options file specified with @`` error message. + File paths should not contain any special character or the compilation might fail with a ``moc: Cannot open options file specified with @`` error message. Qt configuration ================ @@ -92,8 +92,8 @@ main.cpp :language: cpp | Here we include the headers for the class PCLViewer and the headers for QApplication and QMainWindow. -| Then the main functions consists of instanciating a QApplication `a` which manages the GUI application's control flow and main settings. -| A ``PCLViewer`` object called `w` is instanciated and it's method ``show()`` is called. +| Then the main functions consists of instantiating a QApplication `a` which manages the GUI application's control flow and main settings. +| A ``PCLViewer`` object called `w` is instantiated and it's method ``show()`` is called. | Finally we return the state of our program exit through the QApplication `a`. pclviewer.h @@ -194,7 +194,7 @@ Here we connect slots and signals, this links UI actions to functions. Here is a | This is the last part of our constructor; we add the point cloud to the visualizer, call the method ``pSliderValueChanged`` to change the point size to 2. -We finaly reset the camera within the PCL Visualizer not avoid the user having to zoom out and update the qvtkwidget to be +We finally reset the camera within the PCL Visualizer not avoid the user having to zoom out and update the qvtkwidget to be sure the modifications will be displayed. .. literalinclude:: sources/qt_visualizer/pclviewer.cpp diff --git a/doc/tutorials/content/random_sample_consensus.rst b/doc/tutorials/content/random_sample_consensus.rst index f3376b86..0406cf00 100644 --- a/doc/tutorials/content/random_sample_consensus.rst +++ b/doc/tutorials/content/random_sample_consensus.rst @@ -38,7 +38,7 @@ From [Wikipedia]_: :align: right :height: 200px -The pictures to the left and right (From [Wikipedia]_) show a simple application of the RANSAC algorithm on a 2-dimensional set of data. The image on our left is a visual representation of a data set containg both inliers and outliers. The image on our right shows all of the outliers in red, and shows inliers in blue. The blue line is the result of the work done by RANSAC. In this case the model that we are trying to fit to the data is a line, and it looks like it's a fairly good fit to our data. +The pictures to the left and right (From [Wikipedia]_) show a simple application of the RANSAC algorithm on a 2-dimensional set of data. The image on our left is a visual representation of a data set containing both inliers and outliers. The image on our right shows all of the outliers in red, and shows inliers in blue. The blue line is the result of the work done by RANSAC. In this case the model that we are trying to fit to the data is a line, and it looks like it's a fairly good fit to our data. The code -------- @@ -95,7 +95,7 @@ Hit 'r' on your keyboard to scale and center the viewer. You can then click and $ ./random_sample_consensus -f -the program will display only the indices of the original PointCloud which satisfy the paticular model we have chosen (in this case plane) as found by RandomSampleConsens in the viewer. +the program will display only the indices of the original PointCloud which satisfy the particular model we have chosen (in this case plane) as found by RandomSampleConsens in the viewer. .. image:: images/ransac_inliers_plane.png :align: center diff --git a/doc/tutorials/content/range_image_creation.rst b/doc/tutorials/content/range_image_creation.rst index c05b4649..0c4bc599 100644 --- a/doc/tutorials/content/range_image_creation.rst +++ b/doc/tutorials/content/range_image_creation.rst @@ -55,7 +55,7 @@ borderSize greater 0 will leave a border of unobserved points around the image w :language: cpp :lines: 29-33 -The remaining code creates the range image from the point cloud with the given paramters and outputs some information on the terminal. +The remaining code creates the range image from the point cloud with the given parameters and outputs some information on the terminal. The range image is derived from the PointCloud class and its points have the members x,y,z and range. There are three kinds of points. Valid points have a real range greater zero. Unobserved points have x=y=z=NAN and range=-INFINITY. Far range points have x=y=z=NAN and range=INFINITY. diff --git a/doc/tutorials/content/region_growing_rgb_segmentation.rst b/doc/tutorials/content/region_growing_rgb_segmentation.rst index f4c8405d..5b2d6757 100644 --- a/doc/tutorials/content/region_growing_rgb_segmentation.rst +++ b/doc/tutorials/content/region_growing_rgb_segmentation.rst @@ -77,7 +77,7 @@ Here the color threshold for clusters is set. This value is similar to the previ :language: cpp :lines: 37-37 -This value is similar to that which was used in the :ref:`region_growing_segmentation` tutorial. In addition to that, it is used for merging process mentioned in the begining. +This value is similar to that which was used in the :ref:`region_growing_segmentation` tutorial. In addition to that, it is used for merging process mentioned in the beginning. If cluster has less points than was set through ``setMinClusterSize`` method, then it will be merged with the nearest neighbour. .. literalinclude:: sources/region_growing_rgb_segmentation/region_growing_rgb_segmentation.cpp diff --git a/doc/tutorials/content/region_growing_segmentation.rst b/doc/tutorials/content/region_growing_segmentation.rst index 47bdaba8..1bd5dd31 100644 --- a/doc/tutorials/content/region_growing_segmentation.rst +++ b/doc/tutorials/content/region_growing_segmentation.rst @@ -171,7 +171,7 @@ and how to access its elements. :lines: 65-73 The ``pcl::RegionGrowing`` class provides a method that returns the colored cloud where each cluster has its own color. -So in this part of code the ``pcl::visualization::CloudViewer`` is instanciated for viewing the result of the segmentation - the same colored cloud. +So in this part of code the ``pcl::visualization::CloudViewer`` is instantiated for viewing the result of the segmentation - the same colored cloud. You can learn more about cloud visualization in the :ref:`cloud_viewer` tutorial. Compiling and running the program diff --git a/doc/tutorials/content/registration_api.rst b/doc/tutorials/content/registration_api.rst index 0a5b5c15..1988e575 100644 --- a/doc/tutorials/content/registration_api.rst +++ b/doc/tutorials/content/registration_api.rst @@ -58,7 +58,7 @@ The programmer can decide to loop over any or all of the steps. .. image:: images/registration/block_diagram_single_iteration.jpg :align: center -The computational steps for two datasets are straighforward: +The computational steps for two datasets are straightforward: * from a set of points, identify **interest points** (i.e., **keypoints**) that best represent the scene in both datasets; * at each keypoint, compute a **feature descriptor**; diff --git a/doc/tutorials/content/remove_outliers.rst b/doc/tutorials/content/remove_outliers.rst index d558424e..5405fbeb 100644 --- a/doc/tutorials/content/remove_outliers.rst +++ b/doc/tutorials/content/remove_outliers.rst @@ -18,7 +18,7 @@ editor, and place the following inside it: RadiusOutlierRemoval Background ------------------------------- -The picture below helps to visualize what the RadiusOutlierRemoval filter object does. The user specifies a number of neighbors which every indice must have within a specified radius to remain in the PointCloud. For example if 1 neighbor is specified, only the yellow point will be removed from the PointCloud. If 2 neighbors are specified then both the yellow and green points will be removed from the PointCloud. +The picture below helps to visualize what the RadiusOutlierRemoval filter object does. The user specifies a number of neighbors which every index must have within a specified radius to remain in the PointCloud. For example if 1 neighbor is specified, only the yellow point will be removed from the PointCloud. If 2 neighbors are specified then both the yellow and green points will be removed from the PointCloud. .. image:: images/radius_outlier.png @@ -62,7 +62,7 @@ For the *ConditionalRemoval* class, the user must specify '-c' as the command li :language: cpp :lines: 38-53 -Basically, we create the condition which a given point must satisfy for it to remain in our PointCloud. In this example, we use add two comparisons to the conditon: greater than (GT) 0.0 and less than (LT) 0.8. This condition is then used to build the filter. +Basically, we create the condition which a given point must satisfy for it to remain in our PointCloud. In this example, we use add two comparisons to the condition: greater than (GT) 0.0 and less than (LT) 0.8. This condition is then used to build the filter. In both cases the code above creates the filter object that we are going to use and sets certain parameters that are necessary for the filtering to take place. diff --git a/doc/tutorials/content/rops_feature.rst b/doc/tutorials/content/rops_feature.rst index 496f5a3a..c7048d73 100644 --- a/doc/tutorials/content/rops_feature.rst +++ b/doc/tutorials/content/rops_feature.rst @@ -92,7 +92,7 @@ Here is the place where the instantiation of the ``pcl::ROPSEstimation`` class t * PointInT - type of the input points; * PointOutT - type of the output points. -Immediately after that we set the input all the necessary data neede for the feature computation. +Immediately after that we set the input all the necessary data needed for the feature computation. .. literalinclude:: sources/rops_feature/rops_feature.cpp :language: cpp diff --git a/doc/tutorials/content/sources/conditional_euclidean_clustering/conditional_euclidean_clustering.cpp b/doc/tutorials/content/sources/conditional_euclidean_clustering/conditional_euclidean_clustering.cpp index 1dc31e1e..54d8fe59 100644 --- a/doc/tutorials/content/sources/conditional_euclidean_clustering/conditional_euclidean_clustering.cpp +++ b/doc/tutorials/content/sources/conditional_euclidean_clustering/conditional_euclidean_clustering.cpp @@ -21,7 +21,7 @@ enforceIntensitySimilarity (const PointTypeFull& point_a, const PointTypeFull& p bool enforceCurvatureOrIntensitySimilarity (const PointTypeFull& point_a, const PointTypeFull& point_b, float squared_distance) { - Eigen::Map point_a_normal = point_a.normal, point_b_normal = point_b.normal; + Eigen::Map point_a_normal = point_a.getNormalVector3fMap (), point_b_normal = point_b.getNormalVector3fMap (); if (fabs (point_a.intensity - point_b.intensity) < 5.0f) return (true); if (fabs (point_a_normal.dot (point_b_normal)) < 0.05) @@ -32,7 +32,7 @@ enforceCurvatureOrIntensitySimilarity (const PointTypeFull& point_a, const Point bool customRegionGrowing (const PointTypeFull& point_a, const PointTypeFull& point_b, float squared_distance) { - Eigen::Map point_a_normal = point_a.normal, point_b_normal = point_b.normal; + Eigen::Map point_a_normal = point_a.getNormalVector3fMap (), point_b_normal = point_b.getNormalVector3fMap (); if (squared_distance < 10000) { if (fabs (point_a.intensity - point_b.intensity) < 8.0f) diff --git a/doc/tutorials/content/sources/don_segmentation/don_segmentation.cpp b/doc/tutorials/content/sources/don_segmentation/don_segmentation.cpp index 371301d4..7323e0db 100644 --- a/doc/tutorials/content/sources/don_segmentation/don_segmentation.cpp +++ b/doc/tutorials/content/sources/don_segmentation/don_segmentation.cpp @@ -114,7 +114,7 @@ main (int argc, char *argv[]) if (!don.initCompute ()) { - std::cerr << "Error: Could not intialize DoN feature operator" << std::endl; + std::cerr << "Error: Could not initialize DoN feature operator" << std::endl; exit (EXIT_FAILURE); } diff --git a/doc/tutorials/content/sources/global_hypothesis_verification/global_hypothesis_verification.cpp b/doc/tutorials/content/sources/global_hypothesis_verification/global_hypothesis_verification.cpp index 9001addf..5fb24fed 100644 --- a/doc/tutorials/content/sources/global_hypothesis_verification/global_hypothesis_verification.cpp +++ b/doc/tutorials/content/sources/global_hypothesis_verification/global_hypothesis_verification.cpp @@ -96,6 +96,8 @@ float cg_size_ (0.01f); float cg_thresh_ (5.0f); int icp_max_iter_ (5); float icp_corr_distance_ (0.005f); +float hv_resolution_ (0.005f); +float hv_occupancy_grid_resolution_ (0.01f); float hv_clutter_reg_ (5.0f); float hv_inlier_th_ (0.005f); float hv_occlusion_th_ (0.01f); @@ -436,7 +438,8 @@ main (int argc, GoHv.setSceneCloud (scene); // Scene Cloud GoHv.addModels (registered_instances, true); //Models to verify - + GoHv.setResolution (hv_resolution_); + GoHv.setResolutionOccupancyGrid (hv_occupancy_grid_resolution_); GoHv.setInlierThreshold (hv_inlier_th_); GoHv.setOcclusionThreshold (hv_occlusion_th_); GoHv.setRegularizer (hv_regularizer_); diff --git a/doc/tutorials/content/sources/iccv2011/include/registration.h b/doc/tutorials/content/sources/iccv2011/include/registration.h index a95ec4d7..a446656c 100644 --- a/doc/tutorials/content/sources/iccv2011/include/registration.h +++ b/doc/tutorials/content/sources/iccv2011/include/registration.h @@ -48,7 +48,7 @@ computeInitialAlignment (const PointCloudPtr & source_points, const LocalDescrip } /* Use IterativeClosestPoint to find a precise alignment from the source cloud to the target cloud, - * starting with an intial guess + * starting with an initial guess * Inputs: * source_points * The "source" points, i.e., the points that must be transformed to align with the target point cloud diff --git a/doc/tutorials/content/sources/iccv2011/include/segmentation.h b/doc/tutorials/content/sources/iccv2011/include/segmentation.h index d53a71f9..707405ba 100644 --- a/doc/tutorials/content/sources/iccv2011/include/segmentation.h +++ b/doc/tutorials/content/sources/iccv2011/include/segmentation.h @@ -26,7 +26,7 @@ pcl::ModelCoefficients::Ptr fitPlane (const PointCloudPtr & input, float distance_threshold, float max_iterations) { - // Intialize the SACSegmentation object + // Initialize the SACSegmentation object pcl::SACSegmentation seg; seg.setOptimizeCoefficients (true); seg.setModelType (pcl::SACMODEL_PLANE); diff --git a/doc/tutorials/content/sources/iccv2011/src/test_registration.cpp b/doc/tutorials/content/sources/iccv2011/src/test_registration.cpp index 549a04ba..33fb8c38 100644 --- a/doc/tutorials/content/sources/iccv2011/src/test_registration.cpp +++ b/doc/tutorials/content/sources/iccv2011/src/test_registration.cpp @@ -28,7 +28,7 @@ main (int argc, char ** argv) PointCloudPtr tgt_points = loadPoints (argv[2]); Eigen::Matrix4f tform = Eigen::Matrix4f::Identity (); - // Compute the intial alignment + // Compute the initial alignment double min_sample_dist, max_correspondence_dist, nr_iters; bool compute_intial_alignment = pcl::console::parse_3x_arguments (argc, argv, "-i", min_sample_dist, max_correspondence_dist, nr_iters) > 0; diff --git a/doc/tutorials/content/sources/interactive_icp/interactive_icp.cpp b/doc/tutorials/content/sources/interactive_icp/interactive_icp.cpp index 4cd2cdcf..3b48b60b 100644 --- a/doc/tutorials/content/sources/interactive_icp/interactive_icp.cpp +++ b/doc/tutorials/content/sources/interactive_icp/interactive_icp.cpp @@ -116,7 +116,7 @@ main (int argc, // Visualization pcl::visualization::PCLVisualizer viewer ("ICP demo"); - // Create two verticaly separated viewports + // Create two vertically separated viewports int v1 (0); int v2 (1); viewer.createViewPort (0.0, 0.0, 0.5, 1.0, v1); diff --git a/doc/tutorials/content/sources/iros2011/include/registration.h b/doc/tutorials/content/sources/iros2011/include/registration.h index 7ab636ab..0de26408 100644 --- a/doc/tutorials/content/sources/iros2011/include/registration.h +++ b/doc/tutorials/content/sources/iros2011/include/registration.h @@ -35,7 +35,7 @@ computeInitialAlignment (const PointCloudPtr & source_points, const LocalDescrip /* Use IterativeClosestPoint to find a precise alignment from the source cloud to the target cloud, - * starting with an intial guess + * starting with an initial guess * Inputs: * source_points * The "source" points, i.e., the points that must be transformed to align with the target point cloud diff --git a/doc/tutorials/content/sources/iros2011/include/solution/registration.h b/doc/tutorials/content/sources/iros2011/include/solution/registration.h index 862dd9f5..4cf292fc 100644 --- a/doc/tutorials/content/sources/iros2011/include/solution/registration.h +++ b/doc/tutorials/content/sources/iros2011/include/solution/registration.h @@ -48,7 +48,7 @@ computeInitialAlignment (const PointCloudPtr & source_points, const LocalDescrip } /* Use IterativeClosestPoint to find a precise alignment from the source cloud to the target cloud, - * starting with an intial guess + * starting with an initial guess * Inputs: * source_points * The "source" points, i.e., the points that must be transformed to align with the target point cloud diff --git a/doc/tutorials/content/sources/iros2011/include/solution/segmentation.h b/doc/tutorials/content/sources/iros2011/include/solution/segmentation.h index d53a71f9..707405ba 100644 --- a/doc/tutorials/content/sources/iros2011/include/solution/segmentation.h +++ b/doc/tutorials/content/sources/iros2011/include/solution/segmentation.h @@ -26,7 +26,7 @@ pcl::ModelCoefficients::Ptr fitPlane (const PointCloudPtr & input, float distance_threshold, float max_iterations) { - // Intialize the SACSegmentation object + // Initialize the SACSegmentation object pcl::SACSegmentation seg; seg.setOptimizeCoefficients (true); seg.setModelType (pcl::SACMODEL_PLANE); diff --git a/doc/tutorials/content/sources/iros2011/src/test_registration.cpp b/doc/tutorials/content/sources/iros2011/src/test_registration.cpp index d2bd5ace..a1c8c92a 100644 --- a/doc/tutorials/content/sources/iros2011/src/test_registration.cpp +++ b/doc/tutorials/content/sources/iros2011/src/test_registration.cpp @@ -28,7 +28,7 @@ main (int argc, char ** argv) PointCloudPtr tgt_points = loadPoints (argv[2]); Eigen::Matrix4f tform = Eigen::Matrix4f::Identity (); - // Compute the intial alignment + // Compute the initial alignment double min_sample_dist, max_correspondence_dist, nr_iters; bool compute_intial_alignment = pcl::console::parse_3x_arguments (argc, argv, "-i", min_sample_dist, max_correspondence_dist, nr_iters) > 0; diff --git a/doc/tutorials/content/sources/iterative_closest_point/iterative_closest_point.cpp b/doc/tutorials/content/sources/iterative_closest_point/iterative_closest_point.cpp index a16cb200..53a85f87 100644 --- a/doc/tutorials/content/sources/iterative_closest_point/iterative_closest_point.cpp +++ b/doc/tutorials/content/sources/iterative_closest_point/iterative_closest_point.cpp @@ -35,7 +35,7 @@ int std::cout << " " << cloud_out->points[i].x << " " << cloud_out->points[i].y << " " << cloud_out->points[i].z << std::endl; pcl::IterativeClosestPoint icp; - icp.setInputCloud(cloud_in); + icp.setInputSource(cloud_in); icp.setInputTarget(cloud_out); pcl::PointCloud Final; icp.align(Final); diff --git a/doc/tutorials/content/sources/matrix_transform/matrix_transform.cpp b/doc/tutorials/content/sources/matrix_transform/matrix_transform.cpp index a8469a37..70830c9d 100644 --- a/doc/tutorials/content/sources/matrix_transform/matrix_transform.cpp +++ b/doc/tutorials/content/sources/matrix_transform/matrix_transform.cpp @@ -97,7 +97,7 @@ main (int argc, char** argv) // Define a translation of 2.5 meters on the x axis. transform_2.translation() << 2.5, 0.0, 0.0; - // The same rotation matrix as before; theta radians arround Z axis + // The same rotation matrix as before; theta radians around Z axis transform_2.rotate (Eigen::AngleAxisf (theta, Eigen::Vector3f::UnitZ())); // Print the transformation diff --git a/doc/tutorials/content/sources/narf_feature_extraction/narf_feature_extraction.cpp b/doc/tutorials/content/sources/narf_feature_extraction/narf_feature_extraction.cpp index 70d4dbec..b1d246b5 100644 --- a/doc/tutorials/content/sources/narf_feature_extraction/narf_feature_extraction.cpp +++ b/doc/tutorials/content/sources/narf_feature_extraction/narf_feature_extraction.cpp @@ -115,7 +115,7 @@ main (int argc, char** argv) else { setUnseenToMaxRange = true; - cout << "\nNo *.pcd file given => Genarating example point cloud.\n\n"; + cout << "\nNo *.pcd file given => Generating example point cloud.\n\n"; for (float x=-0.5f; x<=0.5f; x+=0.01f) { for (float y=-0.5f; y<=0.5f; y+=0.01f) diff --git a/doc/tutorials/content/sources/narf_keypoint_extraction/narf_keypoint_extraction.cpp b/doc/tutorials/content/sources/narf_keypoint_extraction/narf_keypoint_extraction.cpp index e6ae34f7..48ca901d 100644 --- a/doc/tutorials/content/sources/narf_keypoint_extraction/narf_keypoint_extraction.cpp +++ b/doc/tutorials/content/sources/narf_keypoint_extraction/narf_keypoint_extraction.cpp @@ -109,7 +109,7 @@ main (int argc, char** argv) else { setUnseenToMaxRange = true; - cout << "\nNo *.pcd file given => Genarating example point cloud.\n\n"; + cout << "\nNo *.pcd file given => Generating example point cloud.\n\n"; for (float x=-0.5f; x<=0.5f; x+=0.01f) { for (float y=-0.5f; y<=0.5f; y+=0.01f) diff --git a/doc/tutorials/content/sources/pcl_visualizer/pcl_visualizer_demo.cpp b/doc/tutorials/content/sources/pcl_visualizer/pcl_visualizer_demo.cpp index 200cc36d..f9f6ea04 100644 --- a/doc/tutorials/content/sources/pcl_visualizer/pcl_visualizer_demo.cpp +++ b/doc/tutorials/content/sources/pcl_visualizer/pcl_visualizer_demo.cpp @@ -282,7 +282,7 @@ main (int argc, char** argv) // ------------------------------------ pcl::PointCloud::Ptr basic_cloud_ptr (new pcl::PointCloud); pcl::PointCloud::Ptr point_cloud_ptr (new pcl::PointCloud); - std::cout << "Genarating example point clouds.\n\n"; + std::cout << "Generating example point clouds.\n\n"; // We're going to make an ellipse extruded along the z-axis. The colour for // the XYZRGB cloud will gradually go from red to green to blue. uint8_t r(255), g(15), b(15); diff --git a/doc/tutorials/content/sources/qt_colorize_cloud/CMakeLists.txt b/doc/tutorials/content/sources/qt_colorize_cloud/CMakeLists.txt index 60d27976..bcd391c6 100644 --- a/doc/tutorials/content/sources/qt_colorize_cloud/CMakeLists.txt +++ b/doc/tutorials/content/sources/qt_colorize_cloud/CMakeLists.txt @@ -1,28 +1,31 @@ -cmake_minimum_required (VERSION 2.6 FATAL_ERROR) +cmake_minimum_required(VERSION 2.8.11) -project (pcl-colorize_cloud) -find_package (Qt4 REQUIRED) -find_package (VTK REQUIRED) -find_package (PCL 1.7.1 REQUIRED) +project(pcl_colorize_cloud) -include_directories (${PCL_INCLUDE_DIRS}) -link_directories (${PCL_LIBRARY_DIRS}) -add_definitions (${PCL_DEFINITIONS}) +# init_qt: Let's do the CMake job for us +set(CMAKE_AUTOMOC ON) # For meta object compiler +set(CMAKE_AUTORCC ON) # Resource files +set(CMAKE_AUTOUIC ON) # UI files -set (project_SOURCES main.cpp pclviewer.cpp) -set (project_HEADERS pclviewer.h) -set (project_FORMS pclviewer.ui) -set (VTK_LIBRARIES vtkRendering vtkGraphics vtkHybrid QVTK) +# Find includes in corresponding build directories +set(CMAKE_INCLUDE_CURRENT_DIR ON) -QT4_WRAP_CPP (project_HEADERS_MOC ${project_HEADERS}) -QT4_WRAP_UI (project_FORMS_HEADERS ${project_FORMS}) +# Find the QtWidgets library +find_package(Qt5 REQUIRED Widgets) -INCLUDE (${QT_USE_FILE}) -ADD_DEFINITIONS (${QT_DEFINITIONS}) +find_package(VTK REQUIRED) +find_package(PCL 1.7.1 REQUIRED) -ADD_EXECUTABLE (colorize_cloud ${project_SOURCES} - ${project_FORMS_HEADERS} - ${project_HEADERS_MOC}) +# Fix a compilation bug under ubuntu 16.04 (Xenial) +list(REMOVE_ITEM PCL_LIBRARIES "vtkproj4") -TARGET_LINK_LIBRARIES (colorize_cloud ${QT_LIBRARIES} ${PCL_LIBRARIES} ${VTK_LIBRARIES}) +include_directories(${PCL_INCLUDE_DIRS}) +add_definitions(${PCL_DEFINITIONS}) +set(project_SOURCES main.cpp pclviewer.cpp) + +add_executable(${PROJECT_NAME} ${project_SOURCES}) + +target_link_libraries(${PROJECT_NAME} ${PCL_LIBRARIES}) + +qt5_use_modules(${PROJECT_NAME} Widgets) diff --git a/doc/tutorials/content/sources/qt_colorize_cloud/pclviewer.cpp b/doc/tutorials/content/sources/qt_colorize_cloud/pclviewer.cpp index 7ed3034a..cbb21533 100644 --- a/doc/tutorials/content/sources/qt_colorize_cloud/pclviewer.cpp +++ b/doc/tutorials/content/sources/qt_colorize_cloud/pclviewer.cpp @@ -1,5 +1,5 @@ #include "pclviewer.h" -#include "../build/ui_pclviewer.h" +#include "ui_pclviewer.h" PCLViewer::PCLViewer (QWidget *parent) : QMainWindow (parent), diff --git a/doc/tutorials/content/sources/qt_visualizer/CMakeLists.txt b/doc/tutorials/content/sources/qt_visualizer/CMakeLists.txt index 914dc610..6eea0260 100644 --- a/doc/tutorials/content/sources/qt_visualizer/CMakeLists.txt +++ b/doc/tutorials/content/sources/qt_visualizer/CMakeLists.txt @@ -1,28 +1,31 @@ -cmake_minimum_required (VERSION 2.6 FATAL_ERROR) +cmake_minimum_required(VERSION 2.8.11) -project (pcl-visualizer) -find_package (Qt4 REQUIRED) -find_package (VTK REQUIRED) -find_package (PCL 1.7.1 REQUIRED) +project(pcl_visualizer) -include_directories (${PCL_INCLUDE_DIRS}) -link_directories (${PCL_LIBRARY_DIRS}) -add_definitions (${PCL_DEFINITIONS}) +# init_qt: Let's do the CMake job for us +set(CMAKE_AUTOMOC ON) # For meta object compiler +set(CMAKE_AUTORCC ON) # Resource files +set(CMAKE_AUTOUIC ON) # UI files -set (project_SOURCES main.cpp pclviewer.cpp) -set (project_HEADERS pclviewer.h) -set (project_FORMS pclviewer.ui) -set (VTK_LIBRARIES vtkRendering vtkGraphics vtkHybrid QVTK) +# Find includes in corresponding build directories +set(CMAKE_INCLUDE_CURRENT_DIR ON) -QT4_WRAP_CPP (project_HEADERS_MOC ${project_HEADERS}) -QT4_WRAP_UI (project_FORMS_HEADERS ${project_FORMS}) +# Find the QtWidgets library +find_package(Qt5 REQUIRED Widgets) -INCLUDE (${QT_USE_FILE}) -ADD_DEFINITIONS (${QT_DEFINITIONS}) +find_package(VTK REQUIRED) +find_package(PCL 1.7.1 REQUIRED) -ADD_EXECUTABLE (pcl_visualizer ${project_SOURCES} - ${project_FORMS_HEADERS} - ${project_HEADERS_MOC}) +# Fix a compilation bug under ubuntu 16.04 (Xenial) +list(REMOVE_ITEM PCL_LIBRARIES "vtkproj4") -TARGET_LINK_LIBRARIES (pcl_visualizer ${QT_LIBRARIES} ${PCL_LIBRARIES} ${VTK_LIBRARIES}) +include_directories(${PCL_INCLUDE_DIRS}) +add_definitions(${PCL_DEFINITIONS}) +set(project_SOURCES main.cpp pclviewer.cpp) + +add_executable(${PROJECT_NAME} ${project_SOURCES}) + +target_link_libraries(${PROJECT_NAME} ${PCL_LIBRARIES}) + +qt5_use_modules(${PROJECT_NAME} Widgets) diff --git a/doc/tutorials/content/sources/qt_visualizer/pclviewer.cpp b/doc/tutorials/content/sources/qt_visualizer/pclviewer.cpp index beff918b..a38a40df 100644 --- a/doc/tutorials/content/sources/qt_visualizer/pclviewer.cpp +++ b/doc/tutorials/content/sources/qt_visualizer/pclviewer.cpp @@ -1,5 +1,5 @@ #include "pclviewer.h" -#include "../build/ui_pclviewer.h" +#include "ui_pclviewer.h" PCLViewer::PCLViewer (QWidget *parent) : QMainWindow (parent), diff --git a/doc/tutorials/content/sources/random_sample_consensus/random_sample_consensus.cpp b/doc/tutorials/content/sources/random_sample_consensus/random_sample_consensus.cpp index 6f8eb491..d8f1ca46 100644 --- a/doc/tutorials/content/sources/random_sample_consensus/random_sample_consensus.cpp +++ b/doc/tutorials/content/sources/random_sample_consensus/random_sample_consensus.cpp @@ -87,7 +87,7 @@ main(int argc, char** argv) // copies all inliers of the model computed to another PointCloud pcl::copyPointCloud(*cloud, inliers, *final); - // creates the visualization object and adds either our orignial cloud or all of the inliers + // creates the visualization object and adds either our original cloud or all of the inliers // depending on the command line arguments specified. boost::shared_ptr viewer; if (pcl::console::find_argument (argc, argv, "-f") >= 0 || pcl::console::find_argument (argc, argv, "-sf") >= 0) diff --git a/doc/tutorials/content/sources/range_image_border_extraction/range_image_border_extraction.cpp b/doc/tutorials/content/sources/range_image_border_extraction/range_image_border_extraction.cpp index 5ad46214..6bb00b1e 100644 --- a/doc/tutorials/content/sources/range_image_border_extraction/range_image_border_extraction.cpp +++ b/doc/tutorials/content/sources/range_image_border_extraction/range_image_border_extraction.cpp @@ -92,7 +92,7 @@ main (int argc, char** argv) } else { - cout << "\nNo *.pcd file given => Genarating example point cloud.\n\n"; + cout << "\nNo *.pcd file given => Generating example point cloud.\n\n"; for (float x=-0.5f; x<=0.5f; x+=0.01f) { for (float y=-0.5f; y<=0.5f; y+=0.01f) diff --git a/doc/tutorials/content/sources/range_image_visualization/range_image_visualization.cpp b/doc/tutorials/content/sources/range_image_visualization/range_image_visualization.cpp index 23634f3e..18acdbd3 100644 --- a/doc/tutorials/content/sources/range_image_visualization/range_image_visualization.cpp +++ b/doc/tutorials/content/sources/range_image_visualization/range_image_visualization.cpp @@ -102,7 +102,7 @@ main (int argc, char** argv) } else { - std::cout << "\nNo *.pcd file given => Genarating example point cloud.\n\n"; + std::cout << "\nNo *.pcd file given => Generating example point cloud.\n\n"; for (float x=-0.5f; x<=0.5f; x+=0.01f) { for (float y=-0.5f; y<=0.5f; y+=0.01f) diff --git a/doc/tutorials/content/sources/template_alignment/template_alignment.cpp b/doc/tutorials/content/sources/template_alignment/template_alignment.cpp index 45841f0c..26f8076b 100644 --- a/doc/tutorials/content/sources/template_alignment/template_alignment.cpp +++ b/doc/tutorials/content/sources/template_alignment/template_alignment.cpp @@ -132,7 +132,7 @@ class TemplateAlignment max_correspondence_distance_ (0.01f*0.01f), nr_iterations_ (500) { - // Intialize the parameters in the Sample Consensus Intial Alignment (SAC-IA) algorithm + // Initialize the parameters in the Sample Consensus Initial Alignment (SAC-IA) algorithm sac_ia_.setMinSampleDistance (min_sample_distance_); sac_ia_.setMaxCorrespondenceDistance (max_correspondence_distance_); sac_ia_.setMaximumIterations (nr_iterations_); diff --git a/doc/tutorials/content/statistical_outlier.rst b/doc/tutorials/content/statistical_outlier.rst index 094a8c04..76ee373d 100644 --- a/doc/tutorials/content/statistical_outlier.rst +++ b/doc/tutorials/content/statistical_outlier.rst @@ -28,7 +28,7 @@ deviation, all points whose mean distances are outside an interval defined by the global distances mean and standard deviation can be considered as outliers and trimmed from the dataset. -The following picture show the effects of the sparse outlier analysis and +The following picture shows the effects of the sparse outlier analysis and removal: the original dataset is shown on the left, while the resultant one on the right. The graphic shows the mean k-nearest neighbor distances in a point neighborhood before and after filtering. diff --git a/doc/tutorials/content/supervoxel_clustering.rst b/doc/tutorials/content/supervoxel_clustering.rst index 373b9783..c5577614 100644 --- a/doc/tutorials/content/supervoxel_clustering.rst +++ b/doc/tutorials/content/supervoxel_clustering.rst @@ -21,7 +21,7 @@ Voxel Cloud Connectivity Segmentation (VCCS) is a recent "superpixel" method whi :scale: 50% :align: center - **From right to left, 6 (faces), 18 (faces,egdes), and 26 (faces, edges, vertices) adjacency** + **From right to left, 6 (faces), 18 (faces,edges), and 26 (faces, edges, vertices) adjacency** The adjacency graph of supervoxels (and the underlying voxels) is maintained efficiently within the octree by specifying that neighbors are voxels within R_voxel of one another, where R_voxel specifies the octree leaf resolution. This adjacency graph is used extensively for both the region growing used to generate the supervoxels, as well as determining adjacency of the resulting supervoxels themselves. @@ -103,7 +103,7 @@ We are now ready to setup the supervoxel clustering. We use the class :pcl:`Supe .. important:: - By default, the algorithm will use a special tranform compressing the depth in Z if your input cloud is organized (eg, from an RGBD sensor like the Kinect). You MUST set use_transform to false if you are using an organized cloud which doesn't have the camera at (0,0,0) and depth in positive Z. The transform is specifically designed to help improve Kinect data by increasing voxel bin size as distance from the camera increases. If your cloud is unorganized, this transform will not be used by default, but can be enabled by using setUseSingleCameraTransform(true). + By default, the algorithm will use a special transform compressing the depth in Z if your input cloud is organized (eg, from an RGBD sensor like the Kinect). You MUST set use_transform to false if you are using an organized cloud which doesn't have the camera at (0,0,0) and depth in positive Z. The transform is specifically designed to help improve Kinect data by increasing voxel bin size as distance from the camera increases. If your cloud is unorganized, this transform will not be used by default, but can be enabled by using setUseSingleCameraTransform(true). .. literalinclude:: sources/supervoxel_clustering/supervoxel_clustering.cpp :language: cpp diff --git a/doc/tutorials/content/template_alignment.rst b/doc/tutorials/content/template_alignment.rst index 2f445eec..2eddd292 100644 --- a/doc/tutorials/content/template_alignment.rst +++ b/doc/tutorials/content/template_alignment.rst @@ -137,7 +137,7 @@ We then perform a little pre-processing on the data to get it ready for alignmen :language: cpp :lines: 253-260 -We also downsample the point cloud with a spacing of 5mm, which reduces the ammount of computation that's required. +We also downsample the point cloud with a spacing of 5mm, which reduces the amount of computation that's required. .. literalinclude:: sources/template_alignment/template_alignment.cpp :language: cpp diff --git a/doc/tutorials/content/tracking.rst b/doc/tutorials/content/tracking.rst index 2b846bf4..62b53743 100644 --- a/doc/tutorials/content/tracking.rst +++ b/doc/tutorials/content/tracking.rst @@ -18,7 +18,7 @@ Details The pcl_tracking library contains data structures and mechanism for 3D tracking which uses Particle Filter Algorithm. This tracking will enable you to implement 6D-pose (position and rotation) tracking which is optimized to run in real time. -At each loop, tracking program proceeds along with following algorythm.(see fig2) +At each loop, tracking program proceeds along with following algorithm.(see fig2) 1. (At t = t - 1) At first, using previous Pariticle's information about position and rotation, it will predict each position and rotation of them at the next frame. @@ -97,7 +97,7 @@ In drawParticles function, you can get particles's positions by calling getParti -In drawResult function, you can get model infomation about position and rotation. +In drawResult function, you can get model information about position and rotation. Compiling and running the program --------------------------------- @@ -116,7 +116,7 @@ If you finish saving CMakeLists.txt, let's prepare for running. 3. Don't move the target and the device until you launch tracking program. 4. Output only target point cloud with your other code (See :ref:`planar_segmentation` tutorial) and save as tracking_target.pcd -After you created model point cloud and the executable, you can then launch tracking_sample. Set device_id as second arguement and pcd file's name you made in above 4 as third. +After you created model point cloud and the executable, you can then launch tracking_sample. Set device_id as second argument and pcd file's name you made in above 4 as third. $ ./tracking_sample “#1” tracking_target.pcd diff --git a/doc/tutorials/content/using_pcl_pcl_config.rst b/doc/tutorials/content/using_pcl_pcl_config.rst index f6271ba6..8fc05530 100644 --- a/doc/tutorials/content/using_pcl_pcl_config.rst +++ b/doc/tutorials/content/using_pcl_pcl_config.rst @@ -103,13 +103,13 @@ The executable we are building makes call to PCL functions. So far, we have only included the PCL headers so the compilers knows about the methods we are calling. We need also to make the linker knows about the libraries we are linking against. As said before the, PCL -found libraries are refered to using ``PCL_LIBRARIES`` variable, all +found libraries are referred to using ``PCL_LIBRARIES`` variable, all that remains is to trigger the link operation which we do calling ``target_link_libraries()`` macro. PCLConfig.cmake uses a CMake special feature named `EXPORT` which allows for using others' projects targets as if you built them yourself. When you are using such targets they are called `imported -targets` and acts just like anyother target. +targets` and acts just like any other target. Compiling and running the project --------------------------------- diff --git a/doc/tutorials/content/vfh_estimation.rst b/doc/tutorials/content/vfh_estimation.rst index 5a079c65..734d2b2d 100644 --- a/doc/tutorials/content/vfh_estimation.rst +++ b/doc/tutorials/content/vfh_estimation.rst @@ -112,7 +112,7 @@ Visualizing VFH signatures -------------------------- *libpcl_visualization* contains a special **PCLHistogramVisualization** class, -which is also used by **pcl_viewer** to automaticall display the VFH +which is also used by **pcl_viewer** to automatically display the VFH descriptors as a histogram of float values. For more information, please see http://www.pointclouds.org/documentation/overview/visualization.php. diff --git a/doc/tutorials/content/vfh_recognition.rst b/doc/tutorials/content/vfh_recognition.rst index a248a9ea..41b9b626 100644 --- a/doc/tutorials/content/vfh_recognition.rst +++ b/doc/tutorials/content/vfh_recognition.rst @@ -75,7 +75,7 @@ below: .. image:: images/vfh_recognition/scene_segmented.jpg -Since we're only trying to cover the explicity training/testing of VFH +Since we're only trying to cover the explicit training/testing of VFH signatures in this tutorial, we provide a set of datasets already collected at: `vfh_recognition_tutorial_data.tbz `_. diff --git a/doc/tutorials/content/walkthrough.rst b/doc/tutorials/content/walkthrough.rst index 073c7994..737af0cc 100644 --- a/doc/tutorials/content/walkthrough.rst +++ b/doc/tutorials/content/walkthrough.rst @@ -17,13 +17,13 @@ PCL is split in a number of modular libraries. The most important set of release ======================== ======================== ======================== Filters_ Features_ Keypoints_ |filters_small| |features_small| |keypoints_small| -Registration_ KdTree_ Octree_ +Registration_ KdTree_ Octree_ |registration_small| |kdtree_small| |octree_small| Segmentation_ `Sample Consensus`_ Surface_ |segmentation_small| |sample_consensus_small| |surface_small| -`Range Image`_ `I/O`_ Visualization_ +`Range Image`_ `I/O`_ Visualization_ |range_image_small| |io_small| |visualization_small| -Common_ Search_ +Common Search_ |pcl_logo| |pcl_logo| ======================== ======================== ======================== @@ -69,7 +69,7 @@ Filters .. image:: images/statistical_removal_2.jpg -**Documentation:** http://docs.pointclouds.org/trunk/a02945.html +**Documentation:** http://docs.pointclouds.org/trunk/group__filters.html **Tutorials:** http://pointclouds.org/documentation/tutorials/#filtering-tutorial @@ -82,17 +82,17 @@ Filters **Location:** * MAC OS X (Homebrew installation) - - Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/filters/`` + - Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/filters/`` - Binaries_: ``$(PCL_PREFIX)/bin/`` - ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Linux - - Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/filters/`` + - Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/filters/`` - Binaries_: ``$(PCL_PREFIX)/bin/`` - ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Windows - - Header files: ``$(PCL_DIRECTORY)/include/pcl-1.6/pcl/filters/`` + - Header files: ``$(PCL_DIRECTORY)/include/pcl-$(PCL_VERSION)/pcl/filters/`` - Binaries_: ``$(PCL_DIRECTORY)/bin/`` - - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL 1.6\`` + - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL $(PCL_VERSION)\`` Top_ @@ -124,7 +124,7 @@ Features | -**Documentation:** http://docs.pointclouds.org/trunk/a02944.html +**Documentation:** http://docs.pointclouds.org/trunk/group__features.html **Tutorials:** http://pointclouds.org/documentation/tutorials/#features-tutorial @@ -139,17 +139,17 @@ Features **Location:** * MAC OS X (Homebrew installation) - * Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/features/`` + * Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/features/`` * Binaries_: ``$(PCL_PREFIX)/bin/`` * ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Linux - * Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/filters/`` + * Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/filters/`` * Binaries_: ``$(PCL_PREFIX)/bin/`` * ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Windows - - Header files: ``$(PCL_DIRECTORY)/include/pcl-1.6/pcl/features/`` + - Header files: ``$(PCL_DIRECTORY)/include/pcl-$(PCL_VERSION)/pcl/features/`` - Binaries_: ``$(PCL_DIRECTORY)/bin/`` - - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL 1.6\`` + - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL $(PCL_VERSION)\`` Top_ @@ -168,7 +168,7 @@ Keypoints | -**Documentation:** http://docs.pointclouds.org/trunk/a02949.html +**Documentation:** http://docs.pointclouds.org/trunk/group__keypoints.html **Tutorials:** http://pointclouds.org/documentation/tutorials/#keypoints-tutorial @@ -185,17 +185,17 @@ Keypoints **Location:** * MAC OS X (Homebrew installation) - - Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/keypoints/`` + - Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/keypoints/`` - Binaries_: ``$(PCL_PREFIX)/bin/`` - ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Linux - - Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/filters/`` + - Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/filters/`` - Binaries_: ``$(PCL_PREFIX)/bin/`` - ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Windows - - Header files: ``$(PCL_DIRECTORY)/include/pcl-1.6/pcl/keypoints/`` + - Header files: ``$(PCL_DIRECTORY)/include/pcl-$(PCL_VERSION)/pcl/keypoints/`` - Binaries_: ``$(PCL_DIRECTORY)/bin/`` - - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL 1.6\`` + - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL $(PCL_VERSION)\`` Top_ @@ -218,7 +218,7 @@ Registration | -**Documentation:** http://docs.pointclouds.org/trunk/a02953.html +**Documentation:** http://docs.pointclouds.org/trunk/group__registration.html **Tutorials:** http://pointclouds.org/documentation/tutorials/#registration-tutorial @@ -232,17 +232,17 @@ Registration **Location:** * MAC OS X (Homebrew installation) - - Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/registration/`` + - Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/registration/`` - Binaries_: ``$(PCL_PREFIX)/bin/`` - ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Linux - - Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/filters/`` + - Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/filters/`` - Binaries_: ``$(PCL_PREFIX)/bin/`` - ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Windows - - Header files: ``$(PCL_DIRECTORY)/include/pcl-1.6/pcl/registration/`` + - Header files: ``$(PCL_DIRECTORY)/include/pcl-$(PCL_VERSION)/pcl/registration/`` - Binaries_: ``$(PCL_DIRECTORY)/bin/`` - - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL 1.6\`` + - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL $(PCL_VERSION)\`` Top_ @@ -265,7 +265,7 @@ Kd-tree | -**Documentation:** http://docs.pointclouds.org/trunk/a02948.html +**Documentation:** http://docs.pointclouds.org/trunk/group__kdtree.html **Tutorials:** http://pointclouds.org/documentation/tutorials/#kdtree-tutorial @@ -274,17 +274,17 @@ Kd-tree **Location:** * MAC OS X (Homebrew installation) - - Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/kdtree/`` + - Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/kdtree/`` - Binaries_: ``$(PCL_PREFIX)/bin/`` - ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Linux - - Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/filters/`` + - Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/filters/`` - Binaries_: ``$(PCL_PREFIX)/bin/`` - ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Windows - - Header files: ``$(PCL_DIRECTORY)/include/pcl-1.6/pcl/kdtree/`` + - Header files: ``$(PCL_DIRECTORY)/include/pcl-$(PCL_VERSION)/pcl/kdtree/`` - Binaries_: ``$(PCL_DIRECTORY)/bin/`` - - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL 1.6\`` + - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL $(PCL_VERSION)\`` Top_ @@ -305,7 +305,7 @@ Octree | -**Documentation:** http://docs.pointclouds.org/trunk/a02950.html +**Documentation:** http://docs.pointclouds.org/trunk/group__octree.html **Tutorials:** http://pointclouds.org/documentation/tutorials/#octree-tutorial @@ -314,17 +314,17 @@ Octree **Location:** * MAC OS X (Homebrew installation) - - Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/octree/`` + - Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/octree/`` - Binaries_: ``$(PCL_PREFIX)/bin/`` - ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Linux - - Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/filters/`` + - Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/filters/`` - Binaries_: ``$(PCL_PREFIX)/bin/`` - ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Windows - - Header files: ``$(PCL_DIRECTORY)/include/pcl-1.6/pcl/octree/`` + - Header files: ``$(PCL_DIRECTORY)/include/pcl-$(PCL_VERSION)/pcl/octree/`` - Binaries_: ``$(PCL_DIRECTORY)/bin/`` - - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL 1.6\`` + - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL $(PCL_VERSION)\`` Top_ @@ -346,7 +346,7 @@ Segmentation | -**Documentation:** http://docs.pointclouds.org/trunk/a02956.html +**Documentation:** http://docs.pointclouds.org/trunk/group__segmentation.html **Tutorials:** http://pointclouds.org/documentation/tutorials/#segmentation-tutorial @@ -361,17 +361,17 @@ Segmentation **Location:** * MAC OS X (Homebrew installation) - - Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/segmentation/`` + - Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/segmentation/`` - Binaries_: ``$(PCL_PREFIX)/bin/`` - ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Linux - - Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/filters/`` + - Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/filters/`` - Binaries_: ``$(PCL_PREFIX)/bin/`` - ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Windows - - Header files: ``$(PCL_DIRECTORY)/include/pcl-1.6/pcl/segmentation/`` + - Header files: ``$(PCL_DIRECTORY)/include/pcl-$(PCL_VERSION)/pcl/segmentation/`` - Binaries_: ``$(PCL_DIRECTORY)/bin/`` - - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL 1.6\`` + - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL $(PCL_VERSION)\`` Top_ @@ -392,7 +392,7 @@ Sample Consensus | -**Documentation:** http://docs.pointclouds.org/trunk/a02954.html +**Documentation:** http://docs.pointclouds.org/trunk/group__sample__consensus.html **Tutorials:** http://pointclouds.org/documentation/tutorials/#sample-consensus @@ -401,17 +401,17 @@ Sample Consensus **Location:** * MAC OS X (Homebrew installation) - - Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/sample_consensus/`` + - Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/sample_consensus/`` - Binaries_: ``$(PCL_PREFIX)/bin/`` - ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Linux - - Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/filters/`` + - Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/filters/`` - Binaries_: ``$(PCL_PREFIX)/bin/`` - ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Windows - - Header files: ``$(PCL_DIRECTORY)/include/pcl-1.6/pcl/sample_consensus/`` + - Header files: ``$(PCL_DIRECTORY)/include/pcl-$(PCL_VERSION)/pcl/sample_consensus/`` - Binaries_: ``$(PCL_DIRECTORY)/bin/`` - - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL 1.6\`` + - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL $(PCL_VERSION)\`` Top_ @@ -438,7 +438,7 @@ Surface | -**Documentation:** http://docs.pointclouds.org/trunk/a02957.html +**Documentation:** http://docs.pointclouds.org/trunk/group__surface.html **Tutorials:** http://pointclouds.org/documentation/tutorials/#surface-tutorial @@ -452,17 +452,17 @@ Surface **Location:** * MAC OS X (Homebrew installation) - - Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/surface/`` + - Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/surface/`` - Binaries_: ``$(PCL_PREFIX)/bin/`` - ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Linux - - Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/filters/`` + - Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/filters/`` - Binaries_: ``$(PCL_PREFIX)/bin/`` - ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Windows - - Header files: ``$(PCL_DIRECTORY)/include/pcl-1.6/pcl/surface/`` + - Header files: ``$(PCL_DIRECTORY)/include/pcl-$(PCL_VERSION)/pcl/surface/`` - Binaries_: ``$(PCL_DIRECTORY)/bin/`` - - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL 1.6\`` + - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL $(PCL_VERSION)\`` Top_ @@ -475,12 +475,12 @@ Range Image The *range_image* library contains two classes for representing and working with range images. A range image (or depth map) is an image whose pixel values represent a distance or depth from the sensor's origin. Range images are a common 3D representation and are often generated by stereo or time-of-flight cameras. With knowledge of the camera's intrinsic calibration parameters, a range image can be converted into a point cloud. + Note: *range_image* is now a part of Common_ module. + .. image:: images/range_image.jpg | -**Documentation:** http://docs.pointclouds.org/trunk/a01344.html - **Tutorials:** http://pointclouds.org/documentation/tutorials/#range-images **Interacts with:** Common_ @@ -488,17 +488,17 @@ Range Image **Location:** * MAC OS X (Homebrew installation) - - Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/range_image/`` + - Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/range_image/`` - Binaries_: ``$(PCL_PREFIX)/bin/`` - ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Linux - - Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/filters/`` + - Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/filters/`` - Binaries_: ``$(PCL_PREFIX)/bin/`` - ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Windows - - Header files: ``$(PCL_DIRECTORY)/include/pcl-1.6/pcl/range_image/`` + - Header files: ``$(PCL_DIRECTORY)/include/pcl-$(PCL_VERSION)/pcl/range_image/`` - Binaries_: ``$(PCL_DIRECTORY)/bin/`` - - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL 1.6\`` + - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL $(PCL_VERSION)\`` Top_ @@ -519,7 +519,7 @@ I/O | -**Documentation:** http://docs.pointclouds.org/trunk/a02947.html +**Documentation:** http://docs.pointclouds.org/trunk/group__io.html **Tutorials:** http://pointclouds.org/documentation/tutorials/#i-o @@ -532,17 +532,17 @@ I/O **Location:** * MAC OS X (Homebrew installation) - - Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/io/`` + - Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/io/`` - Binaries_: ``$(PCL_PREFIX)/bin/`` - ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Linux - - Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/filters/`` + - Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/filters/`` - Binaries_: ``$(PCL_PREFIX)/bin/`` - ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Windows - - Header files: ``$(PCL_DIRECTORY)/include/pcl-1.6/pcl/io/`` + - Header files: ``$(PCL_DIRECTORY)/include/pcl-$(PCL_VERSION)/pcl/io/`` - Binaries_: ``$(PCL_DIRECTORY)/bin/`` - - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL 1.6\`` + - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL $(PCL_VERSION)\`` Top_ @@ -586,7 +586,7 @@ Visualization | -**Documentation:** http://docs.pointclouds.org/trunk/a02958.html +**Documentation:** http://docs.pointclouds.org/trunk/group__visualization.html **Tutorials:** http://pointclouds.org/documentation/tutorials/#visualization-tutorial @@ -601,17 +601,17 @@ Visualization **Location:** * MAC OS X (Homebrew installation) - - Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/visualization/`` + - Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/visualization/`` - Binaries_: ``$(PCL_PREFIX)/bin/`` - ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Linux - - Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/filters/`` + - Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/filters/`` - Binaries_: ``$(PCL_PREFIX)/bin/`` - ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Windows - - Header files: ``$(PCL_DIRECTORY)/include/pcl-1.6/pcl/visualization/`` + - Header files: ``$(PCL_DIRECTORY)/include/pcl-$(PCL_VERSION)/pcl/visualization/`` - Binaries_: ``$(PCL_DIRECTORY)/bin/`` - - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL 1.6\`` + - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL $(PCL_VERSION)\`` Top_ @@ -627,17 +627,17 @@ Common **Location:** * MAC OS X (Homebrew installation) - - Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/common/`` + - Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/common/`` - Binaries_: ``$(PCL_PREFIX)/bin/`` - ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Linux - - Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/common/`` + - Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/common/`` - Binaries_: ``$(PCL_PREFIX)/bin/`` - ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Windows - - Header files: ``$(PCL_DIRECTORY)/include/pcl-1.6/pcl/common/`` + - Header files: ``$(PCL_DIRECTORY)/include/pcl-$(PCL_VERSION)/pcl/common/`` - Binaries_: ``$(PCL_DIRECTORY)/bin/`` - - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL 1.6\`` + - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL $(PCL_VERSION)\`` Top_ @@ -665,17 +665,17 @@ Search **Location:** * MAC OS X (Homebrew installation) - - Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/search/`` + - Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/search/`` - Binaries_: ``$(PCL_PREFIX)/bin/`` - ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Linux - - Header files: ``$(PCL_PREFIX)/pcl-1.6/pcl/search/`` + - Header files: ``$(PCL_PREFIX)/pcl-$(PCL_VERSION)/pcl/search/`` - Binaries_: ``$(PCL_PREFIX)/bin/`` - ``$(PCL_PREFIX)`` is the ``cmake`` installation prefix ``CMAKE_INSTALL_PREFIX``, e.g., ``/usr/local/`` * Windows - - Header files: ``$(PCL_DIRECTORY)/include/pcl-1.6/pcl/search/`` + - Header files: ``$(PCL_DIRECTORY)/include/pcl-$(PCL_VERSION)/pcl/search/`` - Binaries_: ``$(PCL_DIRECTORY)/bin/`` - - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL 1.6\`` + - ``$(PCL_DIRECTORY)`` is the PCL installation directory, e.g., ``C:\Program Files\PCL $(PCL_VERSION)\`` Top_ @@ -767,7 +767,7 @@ This section provides a quick reference for some of the common tools in PCL. **Syntax is: mesh2pcd input.{ply,obj} output.pcd **, where options are: - -level X = tesselated sphere level (default: 2) + -level X = tessellated sphere level (default: 2) -resolution X = the sphere resolution in angle increments (default: 100 deg) diff --git a/doc/tutorials/content/writing_new_classes.rst b/doc/tutorials/content/writing_new_classes.rst index 15a38676..ce8879f3 100644 --- a/doc/tutorials/content/writing_new_classes.rst +++ b/doc/tutorials/content/writing_new_classes.rst @@ -163,7 +163,7 @@ Setting up the structure `_ to familiarize yourself with the concepts. -There's two different ways we could set up the structure: i) set up the code +There're two different ways we could set up the structure: i) set up the code separately, as a standalone PCL class, but outside of the PCL code tree; or ii) set up the files directly in the PCL code tree. Since our assumption is that the end result will be contributed back to PCL, it's best to concentrate on the @@ -181,7 +181,7 @@ Assuming that we want the new algorithm to be part of the PCL Filtering library, We also need a name for our new class. Let's call it `BilateralFilter`. -.. [*] The PCL Filtering API specifies that two definitions and implementations must be available for every algorithm: one operating on PointCloud and another one operating on PCLPointCloud2. For the purpose of this tutorial, we will concentrate only on the former. +.. [*] Some PCL filter algorithms provide two implementations: one for PointCloud types and another one operating on legacy PCLPointCloud2 types. This is no longer required. bilateral.h =========== @@ -222,7 +222,7 @@ While we're at it, let's set up two skeleton *bilateral.hpp* and #include - #endif // PCL_FILTERS_BILATERAL_H_ + #endif // PCL_FILTERS_BILATERAL_IMPL_H_ This should be straightforward. We haven't declared any methods for `BilateralFilter` yet, therefore there is no implementation. @@ -239,7 +239,7 @@ Let's write *bilateral.cpp* too: #include Because we are writing templated code in PCL (1.x) where the template parameter -is a point type (see :ref:`adding_custom_ptype`), we want to explicitely +is a point type (see :ref:`adding_custom_ptype`), we want to explicitly instantiate the most common use cases in *bilateral.cpp*, so that users don't have to spend extra cycles when compiling code that uses our `BilateralFilter`. To do this, we need to access both the header @@ -288,7 +288,7 @@ begin filling in the actual code in each file. Let's start with the bilateral.cpp ============= -As previously mentioned, we're going to explicitely instantiate and +As previously mentioned, we're going to explicitly instantiate and *precompile* a number of templated specializations for the `BilateralFilter` class. While this might lead to an increased compilation time for the PCL Filtering library, it will save users the pain of processing and compiling the @@ -349,7 +349,7 @@ that only two of the types contain intensity, namely: Note that at this point we haven't declared the PCL_INSTANTIATE template for `BilateralFilter`, nor did we actually implement the pure virtual functions in -the abstract class :pcl:`pcl::Filter` so attemping to compile the +the abstract class :pcl:`pcl::Filter` so attempting to compile the code will result in errors like:: filters/src/bilateral.cpp:6:32: error: expected constructor, destructor, or type conversion before ‘(’ token @@ -385,7 +385,7 @@ paradigms. } double - getSigmaS () + getSigmaS () const { return (sigma_s_); } @@ -397,7 +397,7 @@ paradigms. } double - getSigmaR () + getSigmaR () const { return (sigma_r_); } @@ -546,7 +546,7 @@ header file becomes: } double - getSigmaS () + getSigmaS () const { return (sigma_s_); } @@ -558,7 +558,7 @@ header file becomes: } double - getSigmaR () + getSigmaR () const { return (sigma_r_); } @@ -589,7 +589,7 @@ header file becomes: bilateral.hpp ============= -There's two methods that we need to implement here, namely `applyFilter` and +There're two methods that we need to implement here, namely `applyFilter` and `computePointWeight`. .. code-block:: cpp @@ -660,7 +660,7 @@ entry for the class: #define PCL_INSTANTIATE_BilateralFilter(T) template class PCL_EXPORTS pcl::BilateralFilter; - #endif // PCL_FILTERS_BILATERAL_H_ + #endif // PCL_FILTERS_BILATERAL_IMPL_H_ One additional thing that we can do is error checking on: @@ -770,7 +770,7 @@ The implementation file header thus becomes: #define PCL_INSTANTIATE_BilateralFilter(T) template class PCL_EXPORTS pcl::BilateralFilter; - #endif // PCL_FILTERS_BILATERAL_H_ + #endif // PCL_FILTERS_BILATERAL_IMPL_H_ Taking advantage of other PCL concepts @@ -885,7 +885,7 @@ The implementation file header thus becomes: #define PCL_INSTANTIATE_BilateralFilter(T) template class PCL_EXPORTS pcl::BilateralFilter; - #endif // PCL_FILTERS_BILATERAL_H_ + #endif // PCL_FILTERS_BILATERAL_IMPL_H_ To make :pcl:`indices_` work without typing the full construct, we need to add a new line to *bilateral.h* that specifies the class @@ -1079,7 +1079,7 @@ class look like: /** \brief Get the half size of the Gaussian bilateral filter window as set by the user. */ double - getHalfSize () + getHalfSize () const { return (sigma_s_); } @@ -1095,7 +1095,7 @@ class look like: /** \brief Get the value of the current standard deviation parameter of the bilateral filter. */ double - getStdDev () + getStdDev () const { return (sigma_r_); } @@ -1133,7 +1133,7 @@ class look like: #endif // PCL_FILTERS_BILATERAL_H_ -And the *bilateral.hpp* like: +And the *bilateral.hpp* likes: .. code-block:: cpp :linenos: @@ -1249,7 +1249,7 @@ And the *bilateral.hpp* like: #define PCL_INSTANTIATE_BilateralFilter(T) template class PCL_EXPORTS pcl::BilateralFilter; - #endif // PCL_FILTERS_BILATERAL_H_ + #endif // PCL_FILTERS_BILATERAL_IMPL_H_ Testing the new class diff --git a/doc/tutorials/content/writing_pcd.rst b/doc/tutorials/content/writing_pcd.rst index 319e013b..b0b98f68 100644 --- a/doc/tutorials/content/writing_pcd.rst +++ b/doc/tutorials/content/writing_pcd.rst @@ -24,26 +24,17 @@ Now, let's break down the code piece by piece. :language: cpp :lines: 2-3 -pcl/io/pcd_io.h is the header that contains the definitions for PCD I/O -operations, and pcl/point_types.h contains definitions for several PointT type -structures (pcl::PointXYZ in our case). +The first file is the header that contains the definitions for PCD I/O +operations, and second one contains definitions for several point type +structures, including ``pcl::PointXYZ`` that we will use. .. literalinclude:: sources/pcd_write/pcd_write.cpp :language: cpp :lines: 8 describes the templated PointCloud structure that we will create. The type of -each point is set to pcl::PointXYZ, which is: - -.. code-block:: cpp - - // \brief A point structure representing Euclidean xyz coordinates. - struct PointXYZ - { - float x; - float y; - float z; - }; +each point is set to ``pcl::PointXYZ``, which is a structure that has ``x``, +``y``, and ``z`` fields. The lines: diff --git a/examples/features/example_difference_of_normals.cpp b/examples/features/example_difference_of_normals.cpp index 24f762a8..237a31ec 100644 --- a/examples/features/example_difference_of_normals.cpp +++ b/examples/features/example_difference_of_normals.cpp @@ -163,7 +163,7 @@ int main (int argc, char *argv[]) don.setNormalScaleSmall(normals_small_scale); if(!don.initCompute ()){ - std::cerr << "Error: Could not intialize DoN feature operator" << std::endl; + std::cerr << "Error: Could not initialize DoN feature operator" << std::endl; exit(EXIT_FAILURE); } diff --git a/examples/features/example_rift_estimation.cpp b/examples/features/example_rift_estimation.cpp index ea813d9a..d35046bb 100644 --- a/examples/features/example_rift_estimation.cpp +++ b/examples/features/example_rift_estimation.cpp @@ -86,7 +86,7 @@ main (int, char** argv) gradient_est.setSearchMethod(treept2); gradient_est.setRadiusSearch(0.25); gradient_est.compute(*cloud_ig); - std::cout<<" Intesity Gradient estimated"; + std::cout<<" Intensity Gradient estimated"; std::cout<<" with size "<< cloud_ig->points.size() <,, - Plane cutting parameters for splitting of segments\n\ - Perform cuts up to this recursion level. Cuts are performed in each segment separately (default 25)\n\ - - Minumum number of supervoxels in the segment to perform cutting (default 400).\n\ - - Minumum score a proposed cut needs to have for being cut (default 0.16)\n\ + - Minimum number of supervoxels in the segment to perform cutting (default 400).\n\ + - Minimum score a proposed cut needs to have for being cut (default 0.16)\n\ -clocal - Use locally constrained cuts (recommended flag)\n\ -cdir - Use directed weigths (recommended flag) \n\ -cclean - Use clean cuts. \n\ @@ -255,7 +255,7 @@ CPCSegmentation Parameters: \n\ { pcl::console::parse (argc, argv, "-o", outputname); - // If no filename is given, get output filename from inputname (strip seperators and file extension) + // If no filename is given, get output filename from inputname (strip separators and file extension) if (outputname.empty () || (outputname.at (0) == '-')) { outputname = pcd_filename; @@ -348,7 +348,7 @@ CPCSegmentation Parameters: \n\ textcolor = bg_white?0:1; pcl::console::print_info ("Maximum cuts: %d\n", max_cuts); - pcl::console::print_info ("Minumum segment siz: %d\n", cutting_min_segments); + pcl::console::print_info ("Minimum segment size: %d\n", cutting_min_segments); pcl::console::print_info ("Use local constrain: %d\n", use_local_constrain); pcl::console::print_info ("Use directed weights: %d\n", use_directed_cutting); pcl::console::print_info ("Use clean cuts: %d\n", use_clean_cutting); @@ -386,7 +386,7 @@ CPCSegmentation Parameters: \n\ /// Get the cloud of supervoxel centroid with normals and the colored cloud with supervoxel coloring (this is used for visulization) pcl::PointCloud::Ptr sv_centroid_normal_cloud = pcl::SupervoxelClustering::makeSupervoxelNormalCloud (supervoxel_clusters); - /// Set paramters for LCCP preprocessing and CPC (CPC inherits from LCCP, thus it includes LCCP's functionality) + /// Set parameters for LCCP preprocessing and CPC (CPC inherits from LCCP, thus it includes LCCP's functionality) PCL_INFO ("Starting Segmentation\n"); pcl::CPCSegmentation cpc; @@ -456,7 +456,7 @@ CPCSegmentation Parameters: \n\ const unsigned char concave_color [3] = {255, 0, 0}; const unsigned char cut_color [3] = { 0,255, 0}; const unsigned char* convex_color = bg_white ? black_color : white_color; - const unsigned char* color; + const unsigned char* color = NULL; //The vertices in the supervoxel adjacency list are the supervoxel centroids //This iterates through them, finding the edges @@ -489,9 +489,13 @@ CPCSegmentation Parameters: \n\ color = concave_color; // two times since we add also two points per edge +#if (VTK_MAJOR_VERSION < 7) || (VTK_MAJOR_VERSION == 7 && VTK_MINOR_VERSION == 0) colors->InsertNextTupleValue (color); colors->InsertNextTupleValue (color); - +#else + colors->InsertNextTypedTuple (color); + colors->InsertNextTypedTuple (color); +#endif pcl::Supervoxel::Ptr supervoxel = supervoxel_clusters.at (sv_label); pcl::PointXYZRGBA vert_curr = supervoxel->centroid_; diff --git a/examples/segmentation/example_extract_clusters_normals.cpp b/examples/segmentation/example_extract_clusters_normals.cpp index da4cc060..faf971e5 100644 --- a/examples/segmentation/example_extract_clusters_normals.cpp +++ b/examples/segmentation/example_extract_clusters_normals.cpp @@ -90,7 +90,7 @@ main (int, char **argv) std::cout << "No of clusters formed are " << cluster_indices.size () << std::endl; - // Saving the clusters in seperate pcd files + // Saving the clusters in separate pcd files int j = 0; for (std::vector::const_iterator it = cluster_indices.begin (); it != cluster_indices.end (); ++it) { diff --git a/examples/segmentation/example_lccp_segmentation.cpp b/examples/segmentation/example_lccp_segmentation.cpp index b8fb70d3..12314275 100644 --- a/examples/segmentation/example_lccp_segmentation.cpp +++ b/examples/segmentation/example_lccp_segmentation.cpp @@ -226,7 +226,7 @@ LCCPSegmentation Parameters: \n\ { pcl::console::parse (argc, argv, "-o", outputname); - // If no filename is given, get output filename from inputname (strip seperators and file extension) + // If no filename is given, get output filename from inputname (strip separators and file extension) if (outputname.empty () || (outputname.at (0) == '-')) { outputname = pcd_filename; @@ -409,8 +409,13 @@ LCCPSegmentation Parameters: \n\ color = concave_color; // two times since we add also two points per edge +#if (VTK_MAJOR_VERSION < 7) || (VTK_MAJOR_VERSION == 7 && VTK_MINOR_VERSION == 0) colors->InsertNextTupleValue (color); colors->InsertNextTupleValue (color); +#else + colors->InsertNextTypedTuple (color); + colors->InsertNextTypedTuple (color); +#endif pcl::Supervoxel::Ptr supervoxel = supervoxel_clusters.at (sv_label); pcl::PointXYZRGBA vert_curr = supervoxel->centroid_; diff --git a/examples/segmentation/example_supervoxels.cpp b/examples/segmentation/example_supervoxels.cpp index a6c43111..bcfb5cf5 100644 --- a/examples/segmentation/example_supervoxels.cpp +++ b/examples/segmentation/example_supervoxels.cpp @@ -230,8 +230,9 @@ main (int argc, char ** argv) new_point.z = depth; } - uint32_t rgb = static_cast(color_pixel[0]) << 16 | static_cast(color_pixel[1]) << 8 | static_cast(color_pixel[2]); - new_point.rgb = *reinterpret_cast (&rgb); + new_point.r = color_pixel[0]; + new_point.g = color_pixel[1]; + new_point.b = color_pixel[2]; cloud->points.push_back (new_point); } diff --git a/features/CMakeLists.txt b/features/CMakeLists.txt index ad5d60bb..d8f51740 100644 --- a/features/CMakeLists.txt +++ b/features/CMakeLists.txt @@ -15,6 +15,7 @@ if(build) "include/pcl/${SUBSYS_NAME}/boost.h" "include/pcl/${SUBSYS_NAME}/eigen.h" "include/pcl/${SUBSYS_NAME}/board.h" + "include/pcl/${SUBSYS_NAME}/flare.h" "include/pcl/${SUBSYS_NAME}/brisk_2d.h" "include/pcl/${SUBSYS_NAME}/cppf.h" "include/pcl/${SUBSYS_NAME}/cvfh.h" @@ -25,6 +26,7 @@ if(build) "include/pcl/${SUBSYS_NAME}/fpfh.h" "include/pcl/${SUBSYS_NAME}/fpfh_omp.h" "include/pcl/${SUBSYS_NAME}/from_meshes.h" + "include/pcl/${SUBSYS_NAME}/gasd.h" "include/pcl/${SUBSYS_NAME}/gfpfh.h" "include/pcl/${SUBSYS_NAME}/integral_image2D.h" "include/pcl/${SUBSYS_NAME}/integral_image_normal.h" @@ -66,6 +68,7 @@ if(build) set(impl_incs "include/pcl/${SUBSYS_NAME}/impl/board.hpp" + "include/pcl/${SUBSYS_NAME}/impl/flare.hpp" "include/pcl/${SUBSYS_NAME}/impl/brisk_2d.hpp" "include/pcl/${SUBSYS_NAME}/impl/cppf.hpp" "include/pcl/${SUBSYS_NAME}/impl/cvfh.hpp" @@ -75,6 +78,7 @@ if(build) "include/pcl/${SUBSYS_NAME}/impl/feature.hpp" "include/pcl/${SUBSYS_NAME}/impl/fpfh.hpp" "include/pcl/${SUBSYS_NAME}/impl/fpfh_omp.hpp" + "include/pcl/${SUBSYS_NAME}/impl/gasd.hpp" "include/pcl/${SUBSYS_NAME}/impl/gfpfh.hpp" "include/pcl/${SUBSYS_NAME}/impl/integral_image2D.hpp" "include/pcl/${SUBSYS_NAME}/impl/integral_image_normal.hpp" @@ -114,6 +118,7 @@ if(build) set(srcs src/board.cpp + src/flare.cpp src/brisk_2d.cpp src/boundary.cpp src/cppf.cpp @@ -122,6 +127,7 @@ if(build) src/crh.cpp src/don.cpp src/fpfh.cpp + src/gasd.cpp src/gfpfh.cpp src/integral_image_normal.cpp src/intensity_gradient.cpp @@ -153,7 +159,7 @@ if(build) ) if(MSVC) - # Workaround to aviod hitting the MSVC 4GB linker memory limit when building pcl_features. + # Workaround to avoid hitting the MSVC 4GB linker memory limit when building pcl_features. # Disable whole program optimization (/GL) and link-time code generation (/LTCG). string(REPLACE "/GL" "" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE}) string(REPLACE "/LTCG" "" CMAKE_SHARED_LINKER_FLAGS_RELEASE ${CMAKE_SHARED_LINKER_FLAGS_RELEASE}) diff --git a/features/include/pcl/features/brisk_2d.h b/features/include/pcl/features/brisk_2d.h index 74cc1a00..a58c24e5 100644 --- a/features/include/pcl/features/brisk_2d.h +++ b/features/include/pcl/features/brisk_2d.h @@ -140,7 +140,7 @@ namespace pcl * * \note This should never be called by a regular user. We use a fixed type in PCL * (BRISKSignature512) and tampering with the parameters might lead to a different - * size descriptor which the user needs to accomodate in a new point type. + * size descriptor which the user needs to accommodate in a new point type. */ void generateKernel (std::vector &radius_list, diff --git a/features/include/pcl/features/cvfh.h b/features/include/pcl/features/cvfh.h index 1e2dfc6d..2ed5738a 100644 --- a/features/include/pcl/features/cvfh.h +++ b/features/include/pcl/features/cvfh.h @@ -201,7 +201,7 @@ namespace pcl min_points_ = min; } - /** \brief Sets wether if the CVFH signatures should be normalized or not + /** \brief Sets whether if the CVFH signatures should be normalized or not * \param[in] normalize true if normalization is required, false otherwise */ inline void @@ -227,7 +227,7 @@ namespace pcl */ float leaf_size_; - /** \brief Wether to normalize the signatures or not. Default: false. */ + /** \brief Whether to normalize the signatures or not. Default: false. */ bool normalize_bins_; /** \brief Curvature threshold for removing normals. */ diff --git a/features/include/pcl/features/flare.h b/features/include/pcl/features/flare.h new file mode 100644 index 00000000..45948b5f --- /dev/null +++ b/features/include/pcl/features/flare.h @@ -0,0 +1,293 @@ +/* +* Software License Agreement (BSD License) +* +* Point Cloud Library (PCL) - www.pointclouds.org +* Copyright (c) 2016-, Open Perception, Inc. +* +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided +* with the distribution. +* * Neither the name of the copyright holder(s) nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +* +*/ + +#ifndef PCL_FLARE_H_ +#define PCL_FLARE_H_ + +#include +#include +#include + + +namespace pcl +{ + + /** \brief FLARELocalReferenceFrameEstimation implements the Fast LocAl Reference framE algorithm + * for local reference frame estimation as described here: + * + * - A. Petrelli, L. Di Stefano, + * "A repeatable and efficient canonical reference for surface matching", + * 3DimPVT, 2012 + * + * FLARE algorithm is deployed in ReLOC algorithm proposed in: + * + * Petrelli A., Di Stefano L., "Pairwise registration by local orientation cues", Computer Graphics Forum, 2015. + * + * \author Alioscia Petrelli + * \ingroup features + */ + template + class FLARELocalReferenceFrameEstimation : public FeatureFromNormals + { + protected: + using Feature::feature_name_; + using Feature::input_; + using Feature::indices_; + using Feature::surface_; + using Feature::tree_; + using Feature::search_parameter_; + using FeatureFromNormals::normals_; + using Feature::fake_surface_; + using Feature::getClassName; + + using typename Feature::PointCloudIn; + using typename Feature::PointCloudOut; + + using typename Feature::PointCloudInConstPtr; + + using typename Feature::KdTreePtr; + + typedef typename pcl::PointCloud PointCloudSignedDistance; + typedef typename PointCloudSignedDistance::Ptr PointCloudSignedDistancePtr; + + typedef boost::shared_ptr > Ptr; + typedef boost::shared_ptr > ConstPtr; + + public: + /** \brief Constructor. */ + FLARELocalReferenceFrameEstimation () : + tangent_radius_ (0.0f), + margin_thresh_ (0.85f), + min_neighbors_for_normal_axis_ (6), + min_neighbors_for_tangent_axis_ (6), + sampled_surface_ (), + sampled_tree_ (), + fake_sampled_surface_ (false) + { + feature_name_ = "FLARELocalReferenceFrameEstimation"; + } + + //Getters/Setters + + /** \brief Set the maximum distance of the points used to estimate the x_axis of the FLARE Reference Frame for a given point. + * + * \param[in] radius The search radius for x axis. + */ + inline void + setTangentRadius (float radius) + { + tangent_radius_ = radius; + } + + /** \brief Get the maximum distance of the points used to estimate the x_axis of the FLARE Reference Frame for a given point. + * + * \return The search radius for x axis. + */ + inline float + getTangentRadius () const + { + return (tangent_radius_); + } + + /** \brief Set the percentage of the search tangent radius after which a point is considered part of the support. + * + * \param[in] margin_thresh the percentage of the search tangent radius after which a point is considered part of the support. + */ + inline void + setMarginThresh (float margin_thresh) + { + margin_thresh_ = margin_thresh; + } + + /** \brief Get the percentage of the search tangent radius after which a point is considered part of the support. + * + * \return The percentage of the search tangent radius after which a point is considered part of the support. + */ + inline float + getMarginThresh () const + { + return (margin_thresh_); + } + + + /** \brief Set min number of neighbours required for the computation of Z axis. + * + * \param[in] min_neighbors_for_normal_axis min number of neighbours required for the computation of Z axis. + */ + inline void + setMinNeighboursForNormalAxis (int min_neighbors_for_normal_axis) + { + min_neighbors_for_normal_axis_ = min_neighbors_for_normal_axis; + } + + /** \brief Get min number of neighbours required for the computation of Z axis. + * + * \return min number of neighbours required for the computation of Z axis. + */ + inline int + getMinNeighboursForNormalAxis () const + { + return (min_neighbors_for_normal_axis_); + } + + + /** \brief Set min number of neighbours required for the computation of X axis. + * + * \param[in] min_neighbors_for_tangent_axis min number of neighbours required for the computation of X axis. + */ + inline void + setMinNeighboursForTangentAxis (int min_neighbors_for_tangent_axis) + { + min_neighbors_for_tangent_axis_ = min_neighbors_for_tangent_axis; + } + + /** \brief Get min number of neighbours required for the computation of X axis. + * + * \return min number of neighbours required for the computation of X axis. + */ + inline int + getMinNeighboursForTangentAxis () const + { + return (min_neighbors_for_tangent_axis_); + } + + + /** \brief Provide a pointer to the dataset used for the estimation of X axis. + * As the estimation of x axis is negligibly affected by surface downsampling, + * this method lets to consider a downsampled version of surface_ in the estimation of x axis. + * This is optional, if this is not set, it will only use the data in the + * surface_ cloud to estimate the x axis. + * \param[in] cloud a pointer to a PointCloud + */ + inline void + setSearchSampledSurface(const PointCloudInConstPtr &cloud) + { + sampled_surface_ = cloud; + fake_sampled_surface_ = false; + } + + /** \brief Get a pointer to the sampled_surface_ cloud dataset. */ + inline const PointCloudInConstPtr& + getSearchSampledSurface() const + { + return (sampled_surface_); + } + + /** \brief Provide a pointer to the search object linked to sampled_surface. + * \param[in] tree a pointer to the spatial search object linked to sampled_surface. + */ + inline void + setSearchMethodForSampledSurface (const KdTreePtr &tree) { sampled_tree_ = tree; } + + /** \brief Get a pointer to the search method used for the extimation of x axis. */ + inline const KdTreePtr& + getSearchMethodForSampledSurface () const + { + return (sampled_tree_); + } + + /** \brief Get the signed distances of the highest points from the fitted planes. */ + inline const std::vector & + getSignedDistancesFromHighestPoints () const + { + return (signed_distances_from_highest_points_); + } + + protected: + /** \brief This method should get called before starting the actual computation. */ + virtual bool + initCompute (); + + /** \brief This method should get called after the actual computation is ended. */ + virtual bool + deinitCompute (); + + /** \brief Estimate the LRF descriptor for a given point based on its spatial neighborhood of 3D points with normals + * \param[in] index the index of the point in input_ + * \param[out] lrf the resultant local reference frame + * \return signed distance of the highest point from the fitted plane. Max if the lrf is not computable. + */ + SignedDistanceT + computePointLRF (const int index, Eigen::Matrix3f &lrf); + + /** \brief Abstract feature estimation method. + * \param[out] output the resultant features + */ + virtual void + computeFeature (PointCloudOut &output); + + + private: + /** \brief Radius used to find tangent axis. */ + float tangent_radius_; + + /** \brief Threshold that define if a support point is near the margins. */ + float margin_thresh_; + + /** \brief Min number of neighbours required for the computation of Z axis. Otherwise, feature point normal is used. */ + int min_neighbors_for_normal_axis_; + + /** \brief Min number of neighbours required for the computation of X axis. Otherwise, a random X axis is set */ + int min_neighbors_for_tangent_axis_; + + /** \brief An input point cloud describing the surface that is to be used + * for nearest neighbor searches for the estimation of X axis. + */ + PointCloudInConstPtr sampled_surface_; + + /** \brief A pointer to the spatial search object used for the estimation of X axis. */ + KdTreePtr sampled_tree_; + + /** \brief Class for normal estimation. */ + NormalEstimation normal_estimation_; + + /** \brief Signed distances of the highest points from the fitted planes.*/ + std::vector signed_distances_from_highest_points_; + + /** \brief If no sampled_surface_ is given, we use surface_ as the sampled surface. */ + bool fake_sampled_surface_; + + }; + +} + +#ifdef PCL_NO_PRECOMPILE +#include +#endif + +#endif //#ifndef PCL_FLARE_H_ diff --git a/features/include/pcl/features/fpfh_omp.h b/features/include/pcl/features/fpfh_omp.h index 4288691d..5a8d7792 100644 --- a/features/include/pcl/features/fpfh_omp.h +++ b/features/include/pcl/features/fpfh_omp.h @@ -60,9 +60,9 @@ namespace pcl * In Proceedings of the 22nd IEEE/RSJ International Conference on Intelligent Robots and Systems (IROS), * St. Louis, MO, USA, October 11-15 2009. * - * \attention + * \attention * The convention for FPFH features is: - * - if a query point's nearest neighbors cannot be estimated, the FPFH feature will be set to NaN + * - if a query point's nearest neighbors cannot be estimated, the FPFH feature will be set to NaN * (not a number) * - it is impossible to estimate a FPFH descriptor for a point that * doesn't have finite 3D coordinates. Therefore, any point that contains @@ -95,16 +95,18 @@ namespace pcl /** \brief Initialize the scheduler and set the number of threads to use. * \param[in] nr_threads the number of hardware threads to use (0 sets the value back to automatic) */ - FPFHEstimationOMP (unsigned int nr_threads = 0) : nr_bins_f1_ (11), nr_bins_f2_ (11), nr_bins_f3_ (11), threads_ (nr_threads) + FPFHEstimationOMP (unsigned int nr_threads = 0) : nr_bins_f1_ (11), nr_bins_f2_ (11), nr_bins_f3_ (11) { feature_name_ = "FPFHEstimationOMP"; + + setNumberOfThreads(nr_threads); } /** \brief Initialize the scheduler and set the number of threads to use. * \param[in] nr_threads the number of hardware threads to use (0 sets the value back to automatic) */ - inline void - setNumberOfThreads (unsigned int nr_threads = 0) { threads_ = nr_threads; } + void + setNumberOfThreads (unsigned int nr_threads = 0); private: /** \brief Estimate the Fast Point Feature Histograms (FPFH) descriptors at a set of points given by @@ -112,7 +114,7 @@ namespace pcl * setSearchMethod () * \param[out] output the resultant point cloud model dataset that contains the FPFH feature estimates */ - void + void computeFeature (PointCloudOut &output); public: diff --git a/features/include/pcl/features/from_meshes.h b/features/include/pcl/features/from_meshes.h index 04229167..f938e66f 100644 --- a/features/include/pcl/features/from_meshes.h +++ b/features/include/pcl/features/from_meshes.h @@ -1,7 +1,8 @@ #ifndef PCL_FEATURES_FROM_MESHES_H_ #define PCL_FEATURES_FROM_MESHES_H_ -#include +#include "pcl/features/normal_3d.h" +#include "pcl/Vertices.h" namespace pcl { diff --git a/features/include/pcl/features/gasd.h b/features/include/pcl/features/gasd.h new file mode 100644 index 00000000..a6f3d9c7 --- /dev/null +++ b/features/include/pcl/features/gasd.h @@ -0,0 +1,366 @@ +/* + * Software License Agreement (BSD License) + * + * Point Cloud Library (PCL) - www.pointclouds.org + * Copyright (c) 2016-, Open Perception, Inc. + * Copyright (c) 2016, Voxar Labs, CIn-UFPE / DEINFO-UFRPE + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the copyright holder(s) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef PCL_FEATURES_GASD_H_ +#define PCL_FEATURES_GASD_H_ + +#include +#include +#include + +namespace pcl +{ + /// Different histogram interpolation methods + enum HistogramInterpolationMethod + { + INTERP_NONE, ///< no interpolation + INTERP_TRILINEAR, ///< trilinear interpolation + INTERP_QUADRILINEAR ///< quadrilinear interpolation + }; + + /** \brief GASDEstimation estimates the Globally Aligned Spatial Distribution (GASD) descriptor for a given + * point cloud dataset given XYZ data. + * + * The suggested PointOutT is pcl::GASDSignature512. + * + * \note If you use this code in any academic work, please cite: + * + * - J. Lima, V. Teichrieb. + * An Efficient Global Point Cloud Descriptor for Object Recognition and Pose Estimation. + * In Proceedings of the 29th SIBGRAPI - Conference on Graphics, Patterns and Images, + * Sao Jose dos Campos, Brazil, October 4-7 2016. + * + * \author Joao Paulo Lima + * + * Voxar Labs, Centro de Informatica, Universidade Federal de Pernambuco, Brazil + * + * Departamento de Estatistica e Informatica, Universidade Federal Rural de Pernambuco, Brazil + * + * \ingroup features + */ + template + class GASDEstimation : public Feature + { + public: + using typename Feature::PointCloudIn; + using typename Feature::PointCloudOut; + typedef boost::shared_ptr > Ptr; + typedef boost::shared_ptr > ConstPtr; + + /** \brief Constructor. + * \param[in] view_direction view direction + * \param[in] shape_half_grid_size shape half grid size + * \param[in] shape_hists_size shape histograms size + * \param[in] shape_interp shape histograms interpolation method + */ + GASDEstimation (const Eigen::Vector3f &view_direction = Eigen::Vector3f (0.0f, 0.0f, 1.0f), + const size_t shape_half_grid_size = 4, + const size_t shape_hists_size = 1, + const HistogramInterpolationMethod shape_interp = INTERP_TRILINEAR) : + view_direction_ (view_direction), + shape_half_grid_size_ (shape_half_grid_size), + shape_hists_size_ (shape_hists_size), + shape_interp_ (shape_interp) + { + search_radius_ = 0; + k_ = 1; + feature_name_ = "GASDEstimation"; + } + + /** \brief Set the view direction. + * \param[in] dir view direction + */ + inline void + setViewDirection (const Eigen::Vector3f &dir) + { + view_direction_ = dir; + } + + /** \brief Set the shape half grid size. + * \param[in] shgs shape half grid size + */ + inline void + setShapeHalfGridSize (const size_t shgs) + { + shape_half_grid_size_ = shgs; + } + + /** \brief Set the shape histograms size. If size is 1, then each histogram bin will store the number + * of points that belong to its correspondent cell in the 3D regular grid. If size > 1, then for each cell + * it will be computed a histogram of normalized distances between each sample and the cloud centroid + * \param[in] shs shape histograms size + */ + inline void + setShapeHistsSize (const size_t shs) + { + shape_hists_size_ = shs; + } + + /** \brief Set the shape histograms interpolation method. + * \param[in] interp shape histograms interpolation method + */ + inline void + setShapeHistsInterpMethod (const HistogramInterpolationMethod interp) + { + shape_interp_ = interp; + } + + /** \brief Returns the transformation aligning the point cloud to the canonical coordinate system + * \param[out] trans transformation + */ + const Eigen::Matrix4f& + getTransform () const + { + return transform_; + } + + /** \brief Overloaded computed method from pcl::Feature. + * \param[out] output the resultant point cloud model dataset containing the estimated feature + */ + void + compute (PointCloudOut &output); + + protected: + using Feature::feature_name_; + using Feature::getClassName; + using Feature::indices_; + using Feature::k_; + using Feature::search_radius_; + using Feature::surface_; + + /** \brief Point cloud aligned to the canonical coordinate system. */ + PointCloudIn shape_samples_; + + /** \brief Normalization factor with respect to axis-aligned bounding cube centered on the origin. */ + float max_coord_; + + /** \brief Normalized sample contribution with respect to the total number of points in the cloud. */ + float hist_incr_; + + /** \brief Current position of output descriptor point cloud. */ + size_t pos_; + + /** \brief add a sample to its respective histogram, optionally performing interpolation. + * \param[in] p histogram sample + * \param[in] max_coord normalization factor with respect to axis-aligned bounding cube centered on the origin + * \param[in] half_grid_size half size of the regular grid used to compute the descriptor + * \param[in] interp interpolation method to be used while computing the descriptor + * \param[in] hbin histogram bin + * \param[in] hist_incr normalization factor of sample contribution + * \param[in,out] hists updated histograms + */ + void + addSampleToHistograms (const Eigen::Vector4f &p, + const float max_coord, + const size_t half_grid_size, + const HistogramInterpolationMethod interp, + const float hbin, + const float hist_incr, + std::vector &hists); + + /** \brief Estimate GASD descriptor + * + * \param[out] output the resultant point cloud model dataset containing the GASD feature + */ + void + computeFeature (PointCloudOut &output); + + private: + /** \brief Transform that aligns the point cloud to the canonical coordinate system. */ + Eigen::Matrix4f transform_; + + /** \brief Viewing direction, default value is (0, 0, 1). */ + Eigen::Vector3f view_direction_; + + /** \brief Half size of the regular grid used to compute the shape descriptor. */ + size_t shape_half_grid_size_; + + /** \brief Size of the histograms of normalized distances between each sample and the cloud centroid. */ + size_t shape_hists_size_; + + /** \brief Interpolation method to be used while computing the shape descriptor. */ + HistogramInterpolationMethod shape_interp_; + + /** \brief Estimates a reference frame for the point cloud and uses it to compute a transform that aligns the point cloud to the canonical coordinate system. */ + void + computeAlignmentTransform (); + + /** \brief copy computed shape histograms to output descriptor point cloud + * \param[in] grid_size size of the regular grid used to compute the descriptor + * \param[in] hists_size size of the shape histograms + * \param[in] hists shape histograms + * \param[out] output output descriptor point cloud + * \param[in,out] pos current position of output descriptor point cloud + */ + void + copyShapeHistogramsToOutput (const size_t grid_size, + const size_t hists_size, + const std::vector &hists, + PointCloudOut &output, + size_t &pos); + }; + + /** \brief GASDColorEstimation estimates the Globally Aligned Spatial Distribution (GASD) descriptor for a given + * point cloud dataset given XYZ and RGB data. + * + * The suggested PointOutT is pcl::GASDSignature984. + * + * \note If you use this code in any academic work, please cite: + * + * - J. Lima, V. Teichrieb. + * An Efficient Global Point Cloud Descriptor for Object Recognition and Pose Estimation. + * In Proceedings of the 29th SIBGRAPI - Conference on Graphics, Patterns and Images, + * Sao Jose dos Campos, Brazil, October 4-7 2016. + * + * \author Joao Paulo Lima + * + * Voxar Labs, Centro de Informatica, Universidade Federal de Pernambuco, Brazil + * + * Departamento de Estatistica e Informatica, Universidade Federal Rural de Pernambuco, Brazil + * + * \ingroup features + */ + template + class GASDColorEstimation : public GASDEstimation + { + public: + using typename Feature::PointCloudOut; + typedef boost::shared_ptr > Ptr; + typedef boost::shared_ptr > ConstPtr; + + /** \brief Constructor. + * \param[in] view_direction view direction + * \param[in] shape_half_grid_size shape half grid size + * \param[in] shape_hists_size shape histograms size + * \param[in] color_half_grid_size color half grid size + * \param[in] color_hists_size color histograms size + * \param[in] shape_interp shape histograms interpolation method + * \param[in] color_interp color histograms interpolation method + */ + GASDColorEstimation (const Eigen::Vector3f &view_direction = Eigen::Vector3f (0.0f, 0.0f, 1.0f), + const size_t shape_half_grid_size = 3, + const size_t shape_hists_size = 1, + const size_t color_half_grid_size = 2, + const size_t color_hists_size = 12, + const HistogramInterpolationMethod shape_interp = INTERP_NONE, + const HistogramInterpolationMethod color_interp = INTERP_NONE) : + GASDEstimation (view_direction, shape_half_grid_size, shape_hists_size, shape_interp), + color_half_grid_size_ (color_half_grid_size), + color_hists_size_ (color_hists_size), + color_interp_ (color_interp) + { + feature_name_ = "GASDColorEstimation"; + } + + /** \brief Set the color half grid size. + * \param[in] chgs color half grid size + */ + inline void + setColorHalfGridSize (const size_t chgs) + { + color_half_grid_size_ = chgs; + } + + /** \brief Set the color histograms size (number of bins in the hue histogram for each cell of the 3D regular grid). + * \param[in] chs color histograms size + */ + inline void + setColorHistsSize (const size_t chs) + { + color_hists_size_ = chs; + } + + /** \brief Set the color histograms interpolation method. + * \param[in] interp color histograms interpolation method + */ + inline void + setColorHistsInterpMethod (const HistogramInterpolationMethod interp) + { + color_interp_ = interp; + } + + protected: + using Feature::feature_name_; + using Feature::getClassName; + using Feature::indices_; + using Feature::k_; + using Feature::search_radius_; + using Feature::surface_; + using GASDEstimation::shape_samples_; + using GASDEstimation::max_coord_; + using GASDEstimation::hist_incr_; + using GASDEstimation::pos_; + + private: + /** \brief Half size of the regular grid used to compute the color descriptor. */ + size_t color_half_grid_size_; + + /** \brief Size of the hue histograms. */ + size_t color_hists_size_; + + /** \brief Interpolation method to be used while computing the color descriptor. */ + HistogramInterpolationMethod color_interp_; + + /** \brief copy computed color histograms to output descriptor point cloud + * \param[in] grid_size size of the regular grid used to compute the descriptor + * \param[in] hists_size size of the color histograms + * \param[in,out] hists color histograms, which are finalized, since they are circular + * \param[out] output output descriptor point cloud + * \param[in,out] pos current position of output descriptor point cloud + */ + void + copyColorHistogramsToOutput (const size_t grid_size, + const size_t hists_size, + std::vector &hists, + PointCloudOut &output, + size_t &pos); + + /** \brief Estimate GASD color descriptor + * + * \param[out] output the resultant point cloud model dataset containing the GASD color feature + */ + void + computeFeature (PointCloudOut &output); + }; +} // namespace pcl + +#ifdef PCL_NO_PRECOMPILE +#include +#endif + +#endif //#ifndef PCL_FEATURES_GASD_H_ diff --git a/features/include/pcl/features/impl/3dsc.hpp b/features/include/pcl/features/impl/3dsc.hpp index e302173c..d1ca769e 100644 --- a/features/include/pcl/features/impl/3dsc.hpp +++ b/features/include/pcl/features/impl/3dsc.hpp @@ -263,7 +263,7 @@ pcl::ShapeContext3DEstimation::computePoint ( PCL_ERROR ("Shape Context Error INF!\n"); if (w != w) PCL_ERROR ("Shape Context Error IND!\n"); - /// Accumulate w into correspondant Bin(j,k,l) + /// Accumulate w into correspondent Bin(j,k,l) desc[(l*elevation_bins_*radius_bins_) + (k*radius_bins_) + j] += w; assert (desc[(l*elevation_bins_*radius_bins_) + (k*radius_bins_) + j] >= 0); diff --git a/features/include/pcl/features/impl/flare.hpp b/features/include/pcl/features/impl/flare.hpp new file mode 100644 index 00000000..6b14652b --- /dev/null +++ b/features/include/pcl/features/impl/flare.hpp @@ -0,0 +1,263 @@ +/* +* Software License Agreement (BSD License) +* +* Point Cloud Library (PCL) - www.pointclouds.org +* Copyright (c) 2016-, Open Perception, Inc. +* +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided +* with the distribution. +* * Neither the name of the copyright holder(s) nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +* +*/ + +#ifndef PCL_FEATURES_IMPL_FLARE_H_ +#define PCL_FEATURES_IMPL_FLARE_H_ + +#include +#include + +////////////////////////////////////////////////////////////////////////////////////////////// +template bool + pcl::FLARELocalReferenceFrameEstimation::initCompute () +{ + if (!FeatureFromNormals::initCompute ()) + { + PCL_ERROR ("[pcl::%s::initCompute] Init failed.\n", getClassName ().c_str ()); + return (false); + } + + if (tangent_radius_ == 0.0f) + { + PCL_ERROR ("[pcl::%s::initCompute] tangent_radius_ not set.\n", getClassName ().c_str ()); + return (false); + } + + // If no search sampled_surface_ has been defined, use the surface_ dataset as the search sampled_surface_ itself + if (!sampled_surface_) + { + fake_sampled_surface_ = true; + sampled_surface_ = surface_; + + if (sampled_tree_) + { + PCL_WARN ("[pcl::%s::initCompute] sampled_surface_ is not set even if sampled_tree_ is already set.", getClassName ().c_str ()); + PCL_WARN ("sampled_tree_ will be rebuilt from surface_. Use sampled_surface_.\n"); + } + } + + // Check if a space search locator was given for sampled_surface_ + if (!sampled_tree_) + { + if (sampled_surface_->isOrganized () && surface_->isOrganized () && input_->isOrganized ()) + sampled_tree_.reset (new pcl::search::OrganizedNeighbor ()); + else + sampled_tree_.reset (new pcl::search::KdTree (false)); + } + + if (sampled_tree_->getInputCloud () != sampled_surface_) // Make sure the tree searches the sampled surface + sampled_tree_->setInputCloud (sampled_surface_); + + return (true); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +template bool + pcl::FLARELocalReferenceFrameEstimation::deinitCompute () +{ + // Reset the surface + if (fake_surface_) + { + surface_.reset (); + fake_surface_ = false; + } + // Reset the sampled surface + if (fake_sampled_surface_) + { + sampled_surface_.reset (); + fake_sampled_surface_ = false; + } + return (true); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +template SignedDistanceT + pcl::FLARELocalReferenceFrameEstimation::computePointLRF (const int index, + Eigen::Matrix3f &lrf) +{ + Eigen::Vector3f x_axis, y_axis; + Eigen::Vector3f fitted_normal; //z_axis + + //find Z axis + + //extract support points for the computation of Z axis + std::vector neighbours_indices; + std::vector neighbours_distances; + int n_neighbours = this->searchForNeighbors (index, search_parameter_, neighbours_indices, neighbours_distances); + + if (n_neighbours < min_neighbors_for_normal_axis_) + { + if (!pcl::isFinite ((*normals_)[index])) + { + //normal is invalid + //setting lrf to NaN + lrf.setConstant (std::numeric_limits::quiet_NaN ()); + return (std::numeric_limits::max ()); + } + + //set z_axis as the normal of index point + fitted_normal = (*normals_)[index].getNormalVector3fMap (); + } + else + { + float plane_curvature; + normal_estimation_.computePointNormal (*surface_, neighbours_indices, fitted_normal (0), fitted_normal (1), fitted_normal (2), plane_curvature); + + //disambiguate Z axis with normal mean + if (!pcl::flipNormalTowardsNormalsMean (*normals_, neighbours_indices, fitted_normal)) + { + //all normals in the neighbourood are invalid + //setting lrf to NaN + lrf.setConstant (std::numeric_limits::quiet_NaN ()); + return (std::numeric_limits::max ()); + } + } + + //setting LRF Z axis + lrf.row (2).matrix () = fitted_normal; + + //find X axis + + //extract support points for Rx radius + n_neighbours = sampled_tree_->radiusSearch ((*input_)[index], tangent_radius_, neighbours_indices, neighbours_distances); + + if (n_neighbours < min_neighbors_for_tangent_axis_) + { + //set X axis as a random axis + x_axis = pcl::geometry::randomOrthogonalAxis (fitted_normal); + y_axis = fitted_normal.cross (x_axis); + + lrf.row (0).matrix () = x_axis; + lrf.row (1).matrix () = y_axis; + + return (std::numeric_limits::max ()); + } + + //find point with the largest signed distance from tangent plane + + SignedDistanceT shape_score; + SignedDistanceT best_shape_score = -std::numeric_limits::max (); + int best_shape_index = -1; + + Eigen::Vector3f best_margin_point; + + const float radius2 = tangent_radius_ * tangent_radius_; + const float margin_distance2 = margin_thresh_ * margin_thresh_ * radius2; + + Vector3fMapConst feature_point = (*input_)[index].getVector3fMap (); + + for (int curr_neigh = 0; curr_neigh < n_neighbours; ++curr_neigh) + { + const int& curr_neigh_idx = neighbours_indices[curr_neigh]; + const float& neigh_distance_sqr = neighbours_distances[curr_neigh]; + + if (neigh_distance_sqr <= margin_distance2) + { + continue; + } + + //point curr_neigh_idx is inside the ring between marginThresh and Radius + + shape_score = fitted_normal.dot ((*sampled_surface_)[curr_neigh_idx].getVector3fMap ()); + + if (shape_score > best_shape_score) + { + best_shape_index = curr_neigh_idx; + best_shape_score = shape_score; + } + } //for each neighbor + + if (best_shape_index == -1) + { + x_axis = pcl::geometry::randomOrthogonalAxis (fitted_normal); + y_axis = fitted_normal.cross (x_axis); + + lrf.row (0).matrix () = x_axis; + lrf.row (1).matrix () = y_axis; + + return (std::numeric_limits::max ()); + } + + //find orthogonal axis directed to best_shape_index point projection on plane with fittedNormal as axis + x_axis = pcl::geometry::projectedAsUnitVector (sampled_surface_->at (best_shape_index).getVector3fMap (), feature_point, fitted_normal); + + y_axis = fitted_normal.cross (x_axis); + + lrf.row (0).matrix () = x_axis; + lrf.row (1).matrix () = y_axis; + //z axis already set + + best_shape_score -= fitted_normal.dot (feature_point); + return (best_shape_score); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +template void + pcl::FLARELocalReferenceFrameEstimation::computeFeature (PointCloudOut &output) +{ + //check whether used with search radius or search k-neighbors + if (this->getKSearch () != 0) + { + PCL_ERROR ( + "[pcl::%s::computeFeature] Error! Search method set to k-neighborhood. Call setKSearch (0) and setRadiusSearch (radius) to use this class.\n", + getClassName ().c_str ()); + return; + } + + signed_distances_from_highest_points_.resize (indices_->size ()); + + for (size_t point_idx = 0; point_idx < indices_->size (); ++point_idx) + { + Eigen::Matrix3f currentLrf; + PointOutT &rf = output[point_idx]; + + signed_distances_from_highest_points_[point_idx] = computePointLRF ((*indices_)[point_idx], currentLrf); + if (signed_distances_from_highest_points_[point_idx] == std::numeric_limits::max ()) + { + output.is_dense = false; + } + + rf.getXAxisVector3fMap () = currentLrf.row (0); + rf.getYAxisVector3fMap () = currentLrf.row (1); + rf.getZAxisVector3fMap () = currentLrf.row (2); + } +} + +#define PCL_INSTANTIATE_FLARELocalReferenceFrameEstimation(T,NT,OutT,SdT) template class PCL_EXPORTS pcl::FLARELocalReferenceFrameEstimation; + +#endif // PCL_FEATURES_IMPL_FLARE_H_ diff --git a/features/include/pcl/features/impl/fpfh_omp.hpp b/features/include/pcl/features/impl/fpfh_omp.hpp index c2904caa..151492b5 100644 --- a/features/include/pcl/features/impl/fpfh_omp.hpp +++ b/features/include/pcl/features/impl/fpfh_omp.hpp @@ -43,6 +43,20 @@ #include +////////////////////////////////////////////////////////////////////////////////////////////// +template void +pcl::FPFHEstimationOMP::setNumberOfThreads (unsigned int nr_threads) +{ + if (nr_threads == 0) +#ifdef _OPENMP + threads_ = omp_get_num_procs(); +#else + threads_ = 1; +#endif + else + threads_ = nr_threads; +} + ////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::FPFHEstimationOMP::computeFeature (PointCloudOut &output) @@ -62,7 +76,8 @@ pcl::FPFHEstimationOMP::computeFeature (PointCloud for (size_t idx = 0; idx < indices_->size (); ++idx) { int p_idx = (*indices_)[idx]; - if (this->searchForNeighbors (p_idx, search_parameter_, nn_indices, nn_dists) == 0) + if (!isFinite ((*input_)[p_idx]) || + this->searchForNeighbors (p_idx, search_parameter_, nn_indices, nn_dists) == 0) continue; spfh_indices_set.insert (nn_indices.begin (), nn_indices.end ()); @@ -98,7 +113,8 @@ pcl::FPFHEstimationOMP::computeFeature (PointCloud int p_idx = spfh_indices_vec[i]; // Find the neighborhood around p_idx - if (this->searchForNeighbors (*surface_, p_idx, search_parameter_, nn_indices, nn_dists) == 0) + if (!isFinite ((*input_)[p_idx]) || + this->searchForNeighbors (*surface_, p_idx, search_parameter_, nn_indices, nn_dists) == 0) continue; // Estimate the SPFH signature around p_idx @@ -108,7 +124,7 @@ pcl::FPFHEstimationOMP::computeFeature (PointCloud spfh_hist_lookup[p_idx] = i; } - // Intialize the array that will store the FPFH signature + // Initialize the array that will store the FPFH signature int nr_bins = nr_bins_f1_ + nr_bins_f2_ + nr_bins_f3_; nn_indices.clear(); diff --git a/features/include/pcl/features/impl/gasd.hpp b/features/include/pcl/features/impl/gasd.hpp new file mode 100644 index 00000000..fbe9a4c7 --- /dev/null +++ b/features/include/pcl/features/impl/gasd.hpp @@ -0,0 +1,402 @@ +/* + * Software License Agreement (BSD License) + * + * Point Cloud Library (PCL) - www.pointclouds.org + * Copyright (c) 2016-, Open Perception, Inc. + * Copyright (c) 2016, Voxar Labs, CIn-UFPE / DEINFO-UFRPE + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the copyright holder(s) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef PCL_FEATURES_IMPL_GASD_H_ +#define PCL_FEATURES_IMPL_GASD_H_ + +#include +#include +#include + +#include + +////////////////////////////////////////////////////////////////////////////////////////////// +template void +pcl::GASDEstimation::compute (PointCloudOut &output) +{ + if (!Feature::initCompute ()) + { + output.width = output.height = 0; + output.points.clear (); + return; + } + + // Resize the output dataset + output.resize (1); + + // Copy header and is_dense flag from input + output.header = surface_->header; + output.is_dense = surface_->is_dense; + + // Perform the actual feature computation + computeFeature (output); + + Feature::deinitCompute (); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +template void +pcl::GASDEstimation::computeAlignmentTransform () +{ + Eigen::Vector4f centroid; + Eigen::Matrix3f covariance_matrix; + + // compute centroid of the object's partial view + pcl::compute3DCentroid (*surface_, *indices_, centroid); + + // compute covariance matrix from points and centroid of the object's partial view + pcl::computeCovarianceMatrix (*surface_, *indices_, centroid, covariance_matrix); + + Eigen::Matrix3f eigenvectors; + Eigen::Vector3f eigenvalues; + + // compute eigenvalues and eigenvectors of the covariance matrix + pcl::eigen33 (covariance_matrix, eigenvectors, eigenvalues); + + // z axis of the reference frame is the eigenvector associated with the minimal eigenvalue + Eigen::Vector3f z_axis = eigenvectors.col (0); + + // if angle between z axis and viewing direction is in the [-90 deg, 90 deg] range, then z axis is negated + if (z_axis.dot (view_direction_) > 0) + { + z_axis = -z_axis; + } + + // x axis of the reference frame is the eigenvector associated with the maximal eigenvalue + const Eigen::Vector3f x_axis = eigenvectors.col (2); + + // y axis is the cross product of z axis and x axis + const Eigen::Vector3f y_axis = z_axis.cross (x_axis); + + const Eigen::Vector3f centroid_xyz = centroid.head<3> (); + + // compute alignment transform from axes and centroid + transform_ << x_axis.transpose (), -x_axis.dot (centroid_xyz), + y_axis.transpose (), -y_axis.dot (centroid_xyz), + z_axis.transpose (), -z_axis.dot (centroid_xyz), + 0.0f, 0.0f, 0.0f, 1.0f; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +template void +pcl::GASDEstimation::addSampleToHistograms (const Eigen::Vector4f &p, + const float max_coord, + const size_t half_grid_size, + const HistogramInterpolationMethod interp, + const float hbin, + const float hist_incr, + std::vector &hists) +{ + const size_t grid_size = half_grid_size * 2; + + // compute normalized coordinates with respect to axis-aligned bounding cube centered on the origin + const Eigen::Vector3f scaled ( (p[0] / max_coord) * half_grid_size, (p[1] / max_coord) * half_grid_size, (p[2] / max_coord) * half_grid_size); + + // compute histograms array coords + Eigen::Vector4f coords (scaled[0] + half_grid_size, scaled[1] + half_grid_size, scaled[2] + half_grid_size, hbin); + + // if using histogram interpolation, subtract 0.5 so samples with the central value of the bin have full weight in it + if (interp != INTERP_NONE) + { + coords -= Eigen::Vector4f (0.5f, 0.5f, 0.5f, 0.5f); + } + + // compute histograms bins indices + const Eigen::Vector4f bins (std::floor (coords[0]), std::floor (coords[1]), std::floor (coords[2]), std::floor (coords[3])); + + // compute indices of the bin where the sample falls into + const size_t grid_idx = ( (bins[0] + 1) * (grid_size + 2) + bins[1] + 1) * (grid_size + 2) + bins[2] + 1; + const size_t h_idx = bins[3] + 1; + + if (interp == INTERP_NONE) + { + // no interpolation + hists[grid_idx][h_idx] += hist_incr; + } + else + { + // if using histogram interpolation, compute trilinear interpolation + coords -= Eigen::Vector4f (bins[0], bins[1], bins[2], 0.0f); + + const float v_x1 = hist_incr * coords[0]; + const float v_x0 = hist_incr - v_x1; + + const float v_xy11 = v_x1 * coords[1]; + const float v_xy10 = v_x1 - v_xy11; + const float v_xy01 = v_x0 * coords[1]; + const float v_xy00 = v_x0 - v_xy01; + + const float v_xyz111 = v_xy11 * coords[2]; + const float v_xyz110 = v_xy11 - v_xyz111; + const float v_xyz101 = v_xy10 * coords[2]; + const float v_xyz100 = v_xy10 - v_xyz101; + const float v_xyz011 = v_xy01 * coords[2]; + const float v_xyz010 = v_xy01 - v_xyz011; + const float v_xyz001 = v_xy00 * coords[2]; + const float v_xyz000 = v_xy00 - v_xyz001; + + if (interp == INTERP_TRILINEAR) + { + // trilinear interpolation + hists[grid_idx][h_idx] += v_xyz000; + hists[grid_idx + 1][h_idx] += v_xyz001; + hists[grid_idx + (grid_size + 2)][h_idx] += v_xyz010; + hists[grid_idx + (grid_size + 3)][h_idx] += v_xyz011; + hists[grid_idx + (grid_size + 2) * (grid_size + 2)][h_idx] += v_xyz100; + hists[grid_idx + (grid_size + 2) * (grid_size + 2) + 1][h_idx] += v_xyz101; + hists[grid_idx + (grid_size + 3) * (grid_size + 2)][h_idx] += v_xyz110; + hists[grid_idx + (grid_size + 3) * (grid_size + 2) + 1][h_idx] += v_xyz111; + } + else + { + // quadrilinear interpolation + coords[3] -= bins[3]; + + const float v_xyzh1111 = v_xyz111 * coords[3]; + const float v_xyzh1110 = v_xyz111 - v_xyzh1111; + const float v_xyzh1101 = v_xyz110 * coords[3]; + const float v_xyzh1100 = v_xyz110 - v_xyzh1101; + const float v_xyzh1011 = v_xyz101 * coords[3]; + const float v_xyzh1010 = v_xyz101 - v_xyzh1011; + const float v_xyzh1001 = v_xyz100 * coords[3]; + const float v_xyzh1000 = v_xyz100 - v_xyzh1001; + const float v_xyzh0111 = v_xyz011 * coords[3]; + const float v_xyzh0110 = v_xyz011 - v_xyzh0111; + const float v_xyzh0101 = v_xyz010 * coords[3]; + const float v_xyzh0100 = v_xyz010 - v_xyzh0101; + const float v_xyzh0011 = v_xyz001 * coords[3]; + const float v_xyzh0010 = v_xyz001 - v_xyzh0011; + const float v_xyzh0001 = v_xyz000 * coords[3]; + const float v_xyzh0000 = v_xyz000 - v_xyzh0001; + + hists[grid_idx][h_idx] += v_xyzh0000; + hists[grid_idx][h_idx + 1] += v_xyzh0001; + hists[grid_idx + 1][h_idx] += v_xyzh0010; + hists[grid_idx + 1][h_idx + 1] += v_xyzh0011; + hists[grid_idx + (grid_size + 2)][h_idx] += v_xyzh0100; + hists[grid_idx + (grid_size + 2)][h_idx + 1] += v_xyzh0101; + hists[grid_idx + (grid_size + 3)][h_idx] += v_xyzh0110; + hists[grid_idx + (grid_size + 3)][h_idx + 1] += v_xyzh0111; + hists[grid_idx + (grid_size + 2) * (grid_size + 2)][h_idx] += v_xyzh1000; + hists[grid_idx + (grid_size + 2) * (grid_size + 2)][h_idx + 1] += v_xyzh1001; + hists[grid_idx + (grid_size + 2) * (grid_size + 2) + 1][h_idx] += v_xyzh1010; + hists[grid_idx + (grid_size + 2) * (grid_size + 2) + 1][h_idx + 1] += v_xyzh1011; + hists[grid_idx + (grid_size + 3) * (grid_size + 2)][h_idx] += v_xyzh1100; + hists[grid_idx + (grid_size + 3) * (grid_size + 2)][h_idx + 1] += v_xyzh1101; + hists[grid_idx + (grid_size + 3) * (grid_size + 2) + 1][h_idx] += v_xyzh1110; + hists[grid_idx + (grid_size + 3) * (grid_size + 2) + 1][h_idx + 1] += v_xyzh1111; + } + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// +template void +pcl::GASDEstimation::copyShapeHistogramsToOutput (const size_t grid_size, + const size_t hists_size, + const std::vector &hists, + PointCloudOut &output, + size_t &pos) +{ + for (size_t i = 0; i < grid_size; ++i) + { + for (size_t j = 0; j < grid_size; ++j) + { + for (size_t k = 0; k < grid_size; ++k) + { + const size_t idx = ( (i + 1) * (grid_size + 2) + (j + 1)) * (grid_size + 2) + (k + 1); + + std::copy (hists[idx].data () + 1, hists[idx].data () + hists_size + 1, output.points[0].histogram + pos); + pos += hists_size; + } + } + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// +template void +pcl::GASDEstimation::computeFeature (PointCloudOut &output) +{ + // compute alignment transform using reference frame + computeAlignmentTransform (); + + // align point cloud + pcl::transformPointCloud (*surface_, *indices_, shape_samples_, transform_); + + const size_t shape_grid_size = shape_half_grid_size_ * 2; + + // each histogram dimension has 2 additional bins, 1 in each boundary, for performing interpolation + std::vector shape_hists ((shape_grid_size + 2) * (shape_grid_size + 2) * (shape_grid_size + 2), + Eigen::VectorXf::Zero (shape_hists_size_ + 2)); + + Eigen::Vector4f centroid_p = Eigen::Vector4f::Zero (); + + // compute normalization factor for distances between samples and centroid + Eigen::Vector4f far_pt; + pcl::getMaxDistance (shape_samples_, centroid_p, far_pt); + far_pt[3] = 0; + const float distance_normalization_factor = (centroid_p - far_pt).norm (); + + // compute normalization factor with respect to axis-aligned bounding cube centered on the origin + Eigen::Vector4f min_pt, max_pt; + pcl::getMinMax3D (shape_samples_, min_pt, max_pt); + + max_coord_ = std::max (min_pt.head<3> ().cwiseAbs ().maxCoeff (), max_pt.head<3> ().cwiseAbs ().maxCoeff ()); + + // normalize sample contribution with respect to the total number of points in the cloud + hist_incr_ = 100.0f / static_cast (shape_samples_.size () - 1); + + // for each sample + for (size_t i = 0; i < shape_samples_.size (); ++i) + { + // compute shape histogram array coord based on distance between sample and centroid + const Eigen::Vector4f p (shape_samples_[i].x, shape_samples_[i].y, shape_samples_[i].z, 0.0f); + const float d = p.norm (); + + const float shape_grid_step = distance_normalization_factor / shape_half_grid_size_; + + const float dist_hist_start = ((int) (d / shape_grid_step)) * shape_grid_step; + + const float dist_hist_val = (d - dist_hist_start) / shape_grid_step; + + const float dbin = dist_hist_val * shape_hists_size_; + + // add sample to shape histograms, optionally performing interpolation + addSampleToHistograms (p, max_coord_, shape_half_grid_size_, shape_interp_, dbin, hist_incr_, shape_hists); + } + + pos_ = 0; + + // copy shape histograms to output + copyShapeHistogramsToOutput (shape_grid_size, shape_hists_size_, shape_hists, output, pos_); + + // set remaining values of the descriptor to zero (if any) + std::fill (output.points[0].histogram + pos_, output.points[0].histogram + output.points[0].descriptorSize (), 0.0f); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +template void +pcl::GASDColorEstimation::copyColorHistogramsToOutput (const size_t grid_size, + const size_t hists_size, + std::vector &hists, + PointCloudOut &output, + size_t &pos) +{ + for (size_t i = 0; i < grid_size; ++i) + { + for (size_t j = 0; j < grid_size; ++j) + { + for (size_t k = 0; k < grid_size; ++k) + { + const size_t idx = ( (i + 1) * (grid_size + 2) + (j + 1)) * (grid_size + 2) + (k + 1); + + hists[idx][1] += hists[idx][hists_size + 1]; + hists[idx][hists_size] += hists[idx][0]; + + std::copy (hists[idx].data () + 1, hists[idx].data () + hists_size + 1, output.points[0].histogram + pos); + pos += hists_size; + } + } + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// +template void +pcl::GASDColorEstimation::computeFeature (PointCloudOut &output) +{ + // call shape feature computation + GASDEstimation::computeFeature (output); + + const size_t color_grid_size = color_half_grid_size_ * 2; + + // each histogram dimension has 2 additional bins, 1 in each boundary, for performing interpolation + std::vector color_hists ((color_grid_size + 2) * (color_grid_size + 2) * (color_grid_size + 2), + Eigen::VectorXf::Zero (color_hists_size_ + 2)); + + // for each sample + for (size_t i = 0; i < shape_samples_.size (); ++i) + { + // compute shape histogram array coord based on distance between sample and centroid + const Eigen::Vector4f p (shape_samples_[i].x, shape_samples_[i].y, shape_samples_[i].z, 0.0f); + + // compute hue value + float hue = 0.f; + + const unsigned char max = std::max (shape_samples_[i].r, std::max (shape_samples_[i].g, shape_samples_[i].b)); + const unsigned char min = std::min (shape_samples_[i].r, std::min (shape_samples_[i].g, shape_samples_[i].b)); + + const float diff_inv = 1.f / static_cast (max - min); + + if (pcl_isfinite (diff_inv)) + { + if (max == shape_samples_[i].r) + { + hue = 60.f * (static_cast (shape_samples_[i].g - shape_samples_[i].b) * diff_inv); + } + else if (max == shape_samples_[i].g) + { + hue = 60.f * (2.f + static_cast (shape_samples_[i].b - shape_samples_[i].r) * diff_inv); + } + else + { + hue = 60.f * (4.f + static_cast (shape_samples_[i].r - shape_samples_[i].g) * diff_inv); // max == b + } + + if (hue < 0.f) + { + hue += 360.f; + } + } + + // compute color histogram array coord based on hue value + const float hbin = (hue / 360) * color_hists_size_; + + // add sample to color histograms, optionally performing interpolation + GASDEstimation::addSampleToHistograms (p, max_coord_, color_half_grid_size_, color_interp_, hbin, hist_incr_, color_hists); + } + + // copy color histograms to output + copyColorHistogramsToOutput (color_grid_size, color_hists_size_, color_hists, output, pos_); + + // set remaining values of the descriptor to zero (if any) + std::fill (output.points[0].histogram + pos_, output.points[0].histogram + output.points[0].descriptorSize (), 0.0f); +} + +#define PCL_INSTANTIATE_GASDEstimation(InT, OutT) template class PCL_EXPORTS pcl::GASDEstimation; +#define PCL_INSTANTIATE_GASDColorEstimation(InT, OutT) template class PCL_EXPORTS pcl::GASDColorEstimation; + +#endif // PCL_FEATURES_IMPL_GASD_H_ diff --git a/features/include/pcl/features/impl/integral_image_normal.hpp b/features/include/pcl/features/impl/integral_image_normal.hpp index fa631f65..fb64c881 100644 --- a/features/include/pcl/features/impl/integral_image_normal.hpp +++ b/features/include/pcl/features/impl/integral_image_normal.hpp @@ -216,7 +216,7 @@ pcl::IntegralImageNormalEstimation::computePointNormal ( unsigned count = integral_image_XYZ_.getFiniteElementsCount (pos_x - (rect_width_2_), pos_y - (rect_height_2_), rect_width_, rect_height_); - // no valid points within the rectangular reagion? + // no valid points within the rectangular region? if (count == 0) { normal.normal_x = normal.normal_y = normal.normal_z = normal.curvature = bad_point; @@ -481,7 +481,7 @@ pcl::IntegralImageNormalEstimation::computePointNormalMirro unsigned count = 0; sumArea(start_x, start_y, end_x, end_y, width, height, boost::bind(&IntegralImage2D::getFiniteElementsCountSE, &integral_image_XYZ_, _1, _2, _3, _4), count); - // no valid points within the rectangular reagion? + // no valid points within the rectangular region? if (count == 0) { normal.normal_x = normal.normal_y = normal.normal_z = normal.curvature = bad_point; diff --git a/features/include/pcl/features/impl/moment_invariants.hpp b/features/include/pcl/features/impl/moment_invariants.hpp index 639b2787..9f4822fb 100644 --- a/features/include/pcl/features/impl/moment_invariants.hpp +++ b/features/include/pcl/features/impl/moment_invariants.hpp @@ -53,7 +53,7 @@ pcl::MomentInvariantsEstimation::computePointMomentInvarian // Estimate the XYZ centroid compute3DCentroid (cloud, indices, xyz_centroid_); - // Initalize the centralized moments + // Initialize the centralized moments float mu200 = 0, mu020 = 0, mu002 = 0, mu110 = 0, mu101 = 0, mu011 = 0; // Iterate over the nearest neighbors set @@ -86,7 +86,7 @@ pcl::MomentInvariantsEstimation::computePointMomentInvarian // Estimate the XYZ centroid compute3DCentroid (cloud, xyz_centroid_); - // Initalize the centralized moments + // Initialize the centralized moments float mu200 = 0, mu020 = 0, mu002 = 0, mu110 = 0, mu101 = 0, mu011 = 0; // Iterate over the nearest neighbors set diff --git a/features/include/pcl/features/impl/normal_3d_omp.hpp b/features/include/pcl/features/impl/normal_3d_omp.hpp index a6bf7d53..cb539b71 100644 --- a/features/include/pcl/features/impl/normal_3d_omp.hpp +++ b/features/include/pcl/features/impl/normal_3d_omp.hpp @@ -43,6 +43,20 @@ #include +/////////////////////////////////////////////////////////////////////////////////////////// +template void +pcl::NormalEstimationOMP::setNumberOfThreads (unsigned int nr_threads) +{ + if (nr_threads == 0) +#ifdef _OPENMP + threads_ = omp_get_num_procs(); +#else + threads_ = 1; +#endif + else + threads_ = nr_threads; +} + /////////////////////////////////////////////////////////////////////////////////////////// template void pcl::NormalEstimationOMP::computeFeature (PointCloudOut &output) @@ -53,7 +67,6 @@ pcl::NormalEstimationOMP::computeFeature (PointCloudOut &ou std::vector nn_dists (k_); output.is_dense = true; - // Save a few cycles by not checking every point for NaN/Inf values if the cloud is set to dense if (input_->is_dense) { @@ -63,7 +76,9 @@ pcl::NormalEstimationOMP::computeFeature (PointCloudOut &ou // Iterating over the entire index vector for (int idx = 0; idx < static_cast (indices_->size ()); ++idx) { - if (this->searchForNeighbors ((*indices_)[idx], search_parameter_, nn_indices, nn_dists) == 0) + Eigen::Vector4f n; + if (this->searchForNeighbors ((*indices_)[idx], search_parameter_, nn_indices, nn_dists) == 0 || + !computePointNormal (*surface_, nn_indices, n, output.points[idx].curvature)) { output.points[idx].normal[0] = output.points[idx].normal[1] = output.points[idx].normal[2] = output.points[idx].curvature = std::numeric_limits::quiet_NaN (); @@ -71,19 +86,13 @@ pcl::NormalEstimationOMP::computeFeature (PointCloudOut &ou continue; } - Eigen::Vector4f n; - pcl::computePointNormal (*surface_, nn_indices, - n, - output.points[idx].curvature); - output.points[idx].normal_x = n[0]; output.points[idx].normal_y = n[1]; output.points[idx].normal_z = n[2]; - + flipNormalTowardsViewpoint (input_->points[(*indices_)[idx]], vpx_, vpy_, vpz_, - output.points[idx].normal[0], - output.points[idx].normal[1], - output.points[idx].normal[2]); + output.points[idx].normal[0], output.points[idx].normal[1], output.points[idx].normal[2]); + } } else @@ -91,11 +100,13 @@ pcl::NormalEstimationOMP::computeFeature (PointCloudOut &ou #ifdef _OPENMP #pragma omp parallel for shared (output) private (nn_indices, nn_dists) num_threads(threads_) #endif - // Iterating over the entire index vector + // Iterating over the entire index vector for (int idx = 0; idx < static_cast (indices_->size ()); ++idx) { + Eigen::Vector4f n; if (!isFinite ((*input_)[(*indices_)[idx]]) || - this->searchForNeighbors ((*indices_)[idx], search_parameter_, nn_indices, nn_dists) == 0) + this->searchForNeighbors ((*indices_)[idx], search_parameter_, nn_indices, nn_dists) == 0 || + !computePointNormal (*surface_, nn_indices, n, output.points[idx].curvature)) { output.points[idx].normal[0] = output.points[idx].normal[1] = output.points[idx].normal[2] = output.points[idx].curvature = std::numeric_limits::quiet_NaN (); @@ -103,19 +114,15 @@ pcl::NormalEstimationOMP::computeFeature (PointCloudOut &ou continue; } - Eigen::Vector4f n; - pcl::computePointNormal (*surface_, nn_indices, - n, - output.points[idx].curvature); - output.points[idx].normal_x = n[0]; output.points[idx].normal_y = n[1]; output.points[idx].normal_z = n[2]; flipNormalTowardsViewpoint (input_->points[(*indices_)[idx]], vpx_, vpy_, vpz_, output.points[idx].normal[0], output.points[idx].normal[1], output.points[idx].normal[2]); + } - } + } } #define PCL_INSTANTIATE_NormalEstimationOMP(T,NT) template class PCL_EXPORTS pcl::NormalEstimationOMP; diff --git a/features/include/pcl/features/impl/shot_lrf_omp.hpp b/features/include/pcl/features/impl/shot_lrf_omp.hpp index 42a3c9ea..ac0127ed 100644 --- a/features/include/pcl/features/impl/shot_lrf_omp.hpp +++ b/features/include/pcl/features/impl/shot_lrf_omp.hpp @@ -44,8 +44,22 @@ #include #include -template -void +////////////////////////////////////////////////////////////////////////////////////////////// +template void +pcl::SHOTLocalReferenceFrameEstimationOMP::setNumberOfThreads (unsigned int nr_threads) +{ + if (nr_threads == 0) +#ifdef _OPENMP + threads_ = omp_get_num_procs(); +#else + threads_ = 1; +#endif + else + threads_ = nr_threads; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +template void pcl::SHOTLocalReferenceFrameEstimationOMP::computeFeature (PointCloudOut &output) { //check whether used with search radius or search k-neighbors diff --git a/features/include/pcl/features/impl/shot_omp.hpp b/features/include/pcl/features/impl/shot_omp.hpp index 16215c3a..d4aa094d 100644 --- a/features/include/pcl/features/impl/shot_omp.hpp +++ b/features/include/pcl/features/impl/shot_omp.hpp @@ -119,6 +119,20 @@ pcl::SHOTColorEstimationOMP::initCompute return (true); } +////////////////////////////////////////////////////////////////////////////////////////////// +template void +pcl::SHOTEstimationOMP::setNumberOfThreads (unsigned int nr_threads) +{ + if (nr_threads == 0) +#ifdef _OPENMP + threads_ = omp_get_num_procs(); +#else + threads_ = 1; +#endif + else + threads_ = nr_threads; +} + ////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::SHOTEstimationOMP::computeFeature (PointCloudOut &output) @@ -189,6 +203,20 @@ pcl::SHOTEstimationOMP::computeFeature ( } } +////////////////////////////////////////////////////////////////////////////////////////////// +template void +pcl::SHOTColorEstimationOMP::setNumberOfThreads (unsigned int nr_threads) +{ + if (nr_threads == 0) +#ifdef _OPENMP + threads_ = omp_get_num_procs(); +#else + threads_ = 1; +#endif + else + threads_ = nr_threads; +} + ////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::SHOTColorEstimationOMP::computeFeature (PointCloudOut &output) diff --git a/features/include/pcl/features/impl/usc.hpp b/features/include/pcl/features/impl/usc.hpp index 633cb7b9..6e2e38b0 100644 --- a/features/include/pcl/features/impl/usc.hpp +++ b/features/include/pcl/features/impl/usc.hpp @@ -235,7 +235,7 @@ pcl::UniqueShapeContext::computePointDescriptor ( PCL_ERROR ("Shape Context Error INF!\n"); if (w != w) PCL_ERROR ("Shape Context Error IND!\n"); - /// Accumulate w into correspondant Bin(j,k,l) + /// Accumulate w into correspondent Bin(j,k,l) desc[(l*elevation_bins_*radius_bins_) + (k*radius_bins_) + j] += w; assert (desc[(l*elevation_bins_*radius_bins_) + (k*radius_bins_) + j] >= 0); diff --git a/features/include/pcl/features/impl/vfh.hpp b/features/include/pcl/features/impl/vfh.hpp index bca6eddf..afa520a8 100644 --- a/features/include/pcl/features/impl/vfh.hpp +++ b/features/include/pcl/features/impl/vfh.hpp @@ -182,7 +182,7 @@ template void pcl::VFHEstimation::computeFeature (PointCloudOut &output) { // ---[ Step 1a : compute the centroid in XYZ space - Eigen::Vector4f xyz_centroid; + Eigen::Vector4f xyz_centroid (0, 0, 0, 0); if (use_given_centroid_) xyz_centroid = centroid_to_use_; diff --git a/features/include/pcl/features/moment_invariants.h b/features/include/pcl/features/moment_invariants.h index 047e27dc..8edd0737 100644 --- a/features/include/pcl/features/moment_invariants.h +++ b/features/include/pcl/features/moment_invariants.h @@ -104,7 +104,6 @@ namespace pcl */ void computeFeature (PointCloudOut &output); - private: /** \brief 16-bytes aligned placeholder for the XYZ centroid of a surface patch. */ Eigen::Vector4f xyz_centroid_; diff --git a/features/include/pcl/features/normal_3d.h b/features/include/pcl/features/normal_3d.h index 82f538d4..8512d395 100644 --- a/features/include/pcl/features/normal_3d.h +++ b/features/include/pcl/features/normal_3d.h @@ -186,6 +186,49 @@ namespace pcl } } + /** \brief Flip (in place) normal to get the same sign of the mean of the normals specified by normal_indices. + * + * The method is described in: + * A. Petrelli, L. Di Stefano, "A repeatable and efficient canonical reference for surface matching", 3DimPVT, 2012 + * A. Petrelli, L. Di Stefano, "On the repeatability of the local reference frame for partial shape matching", 13th International Conference on Computer Vision (ICCV), 2011 + * + * Normals should be unit vectors. Otherwise the resulting mean would be weighted by the normal norms. + * \param[in] normal_cloud Cloud of normals used to compute the mean + * \param[in] normal_indices Indices of normals used to compute the mean + * \param[in] normal input Normal to flip. Normal is modified by the function. + * \return false if normal_indices does not contain any valid normal. + * \ingroup features + */ + template inline bool + flipNormalTowardsNormalsMean ( pcl::PointCloud const &normal_cloud, + std::vector const &normal_indices, + Eigen::Vector3f &normal) + { + Eigen::Vector3f normal_mean = Eigen::Vector3f::Zero (); + + for (size_t i = 0; i < normal_indices.size (); ++i) + { + const PointNT& cur_pt = normal_cloud[normal_indices[i]]; + + if (pcl::isFinite (cur_pt)) + { + normal_mean += cur_pt.getNormalVector3fMap (); + } + } + + if (normal_mean.isZero ()) + return false; + + normal_mean.normalize (); + + if (normal.dot (normal_mean) < 0) + { + normal = -normal; + } + + return true; + } + /** \brief NormalEstimation estimates local surface properties (surface normals and curvatures)at each * 3D point. If PointOutT is specified as pcl::Normal, the normal is stored in the first 3 components (0-2), * and the curvature is stored in component 3. diff --git a/features/include/pcl/features/normal_3d_omp.h b/features/include/pcl/features/normal_3d_omp.h index e98f1c5d..66256991 100644 --- a/features/include/pcl/features/normal_3d_omp.h +++ b/features/include/pcl/features/normal_3d_omp.h @@ -74,16 +74,18 @@ namespace pcl /** \brief Initialize the scheduler and set the number of threads to use. * \param nr_threads the number of hardware threads to use (0 sets the value back to automatic) */ - NormalEstimationOMP (unsigned int nr_threads = 0) : threads_ (nr_threads) + NormalEstimationOMP (unsigned int nr_threads = 0) { feature_name_ = "NormalEstimationOMP"; + + setNumberOfThreads(nr_threads); } /** \brief Initialize the scheduler and set the number of threads to use. * \param nr_threads the number of hardware threads to use (0 sets the value back to automatic) */ - inline void - setNumberOfThreads (unsigned int nr_threads = 0) { threads_ = nr_threads; } + void + setNumberOfThreads (unsigned int nr_threads = 0); protected: /** \brief The number of threads the scheduler should use. */ @@ -94,7 +96,7 @@ namespace pcl * setSearchSurface () and the spatial locator in setSearchMethod () * \param output the resultant point cloud model dataset that contains surface normals and curvatures */ - void + void computeFeature (PointCloudOut &output); }; } diff --git a/features/include/pcl/features/our_cvfh.h b/features/include/pcl/features/our_cvfh.h index 39a93332..60ef67a3 100644 --- a/features/include/pcl/features/our_cvfh.h +++ b/features/include/pcl/features/our_cvfh.h @@ -245,7 +245,7 @@ namespace pcl min_points_ = min; } - /** \brief Sets wether if the signatures should be normalized or not + /** \brief Sets whether the signatures should be normalized or not * \param[in] normalize true if normalization is required, false otherwise */ inline void @@ -335,7 +335,7 @@ namespace pcl */ float leaf_size_; - /** \brief Wether to normalize the signatures or not. Default: false. */ + /** \brief Whether to normalize the signatures or not. Default: false. */ bool normalize_bins_; /** \brief Curvature threshold for removing normals. */ diff --git a/features/include/pcl/features/rops_estimation.h b/features/include/pcl/features/rops_estimation.h index 4e270958..8e6a8a56 100644 --- a/features/include/pcl/features/rops_estimation.h +++ b/features/include/pcl/features/rops_estimation.h @@ -79,7 +79,7 @@ namespace pcl void setNumberOfPartitionBins (unsigned int number_of_bins); - /** \brief Returns the nmber of partition bins. */ + /** \brief Returns the number of partition bins. */ unsigned int getNumberOfPartitionBins () const; @@ -110,7 +110,7 @@ namespace pcl setTriangles (const std::vector & triangles); /** \brief Returns the triangles of the mesh. - * \param[out] triangles triangles of tthe mesh + * \param[out] triangles triangles of the mesh */ void getTriangles (std::vector & triangles) const; @@ -125,14 +125,14 @@ namespace pcl /** \brief This method simply builds the list of triangles for every point. * The list of triangles for each point consists of indices of triangles it belongs to. - * The only purpose of this method is to improve perfomance of the algorithm. + * The only purpose of this method is to improve performance of the algorithm. */ void buildListOfPointsTriangles (); /** \brief This method crops all the triangles within the given radius of the given point. * \param[in] point point for which the local surface is computed - * \param[out] local_triangles strores the indices of the triangles that belong to the local surface + * \param[out] local_triangles stores the indices of the triangles that belong to the local surface * \param[out] local_points stores the indices of the points that belong to the local surface */ void @@ -141,7 +141,7 @@ namespace pcl /** \brief This method computes LRF (Local Reference Frame) matrix for the given point. * \param[in] point point for which the LRF is computed * \param[in] local_triangles list of triangles that represents the local surface of the point - * \paran[out] lrf_matrix strores computed LRF matrix for the given point + * \paran[out] lrf_matrix stores computed LRF matrix for the given point */ void computeLRF (const PointInT& point, const std::set & local_triangles, Eigen::Matrix3f& lrf_matrix) const; @@ -215,10 +215,10 @@ namespace pcl /** \brief Stores the angle step. Step is calculated with respect to number of rotations. */ float step_; - /** \brief Stores the set of triangles reprsenting the mesh. */ + /** \brief Stores the set of triangles representing the mesh. */ std::vector triangles_; - /** \brief Stores the set of triangles for each point. Its purpose is to improve perfomance. */ + /** \brief Stores the set of triangles for each point. Its purpose is to improve performance. */ std::vector > triangles_of_the_point_; public: diff --git a/features/include/pcl/features/rsd.h b/features/include/pcl/features/rsd.h index b51e436f..3d648c0a 100644 --- a/features/include/pcl/features/rsd.h +++ b/features/include/pcl/features/rsd.h @@ -47,7 +47,7 @@ namespace pcl { /** \brief Transform a list of 2D matrices into a point cloud containing the values in a vector (Histogram). * Can be used to transform the 2D histograms obtained in \ref RSDEstimation into a point cloud. - * @note The template paramter N should be (greater or) equal to the product of the number of rows and columns. + * @note The template parameter N should be (greater or) equal to the product of the number of rows and columns. * \param[in] histograms2D the list of neighborhood 2D histograms * \param[out] histogramsPC the dataset containing the linearized matrices * \ingroup features diff --git a/features/include/pcl/features/shot_lrf_omp.h b/features/include/pcl/features/shot_lrf_omp.h index 958b8b7d..1b6a0358 100644 --- a/features/include/pcl/features/shot_lrf_omp.h +++ b/features/include/pcl/features/shot_lrf_omp.h @@ -70,19 +70,21 @@ namespace pcl typedef boost::shared_ptr > Ptr; typedef boost::shared_ptr > ConstPtr; /** \brief Constructor */ - SHOTLocalReferenceFrameEstimationOMP () : threads_ (0) + SHOTLocalReferenceFrameEstimationOMP () { feature_name_ = "SHOTLocalReferenceFrameEstimationOMP"; + + setNumberOfThreads(0); } - + /** \brief Empty destructor */ virtual ~SHOTLocalReferenceFrameEstimationOMP () {} /** \brief Initialize the scheduler and set the number of threads to use. * \param nr_threads the number of hardware threads to use (0 sets the value back to automatic) */ - inline void - setNumberOfThreads (unsigned int nr_threads = 0) { threads_ = nr_threads; } + void + setNumberOfThreads (unsigned int nr_threads = 0); protected: using Feature::feature_name_; diff --git a/features/include/pcl/features/shot_omp.h b/features/include/pcl/features/shot_omp.h index 7fc34b47..97b999be 100644 --- a/features/include/pcl/features/shot_omp.h +++ b/features/include/pcl/features/shot_omp.h @@ -96,13 +96,16 @@ namespace pcl typedef typename Feature::PointCloudIn PointCloudIn; /** \brief Empty constructor. */ - SHOTEstimationOMP (unsigned int nr_threads = 0) : SHOTEstimation (), threads_ (nr_threads) - { }; + SHOTEstimationOMP (unsigned int nr_threads = 0) : SHOTEstimation () + { + setNumberOfThreads(nr_threads); + }; + /** \brief Initialize the scheduler and set the number of threads to use. * \param nr_threads the number of hardware threads to use (0 sets the value back to automatic) */ - inline void - setNumberOfThreads (unsigned int nr_threads = 0) { threads_ = nr_threads; } + void + setNumberOfThreads (unsigned int nr_threads = 0); protected: @@ -178,15 +181,16 @@ namespace pcl SHOTColorEstimationOMP (bool describe_shape = true, bool describe_color = true, unsigned int nr_threads = 0) - : SHOTColorEstimation (describe_shape, describe_color), threads_ (nr_threads) + : SHOTColorEstimation (describe_shape, describe_color) { + setNumberOfThreads(nr_threads); } /** \brief Initialize the scheduler and set the number of threads to use. * \param nr_threads the number of hardware threads to use (0 sets the value back to automatic) */ - inline void - setNumberOfThreads (unsigned int nr_threads = 0) { threads_ = nr_threads; } + void + setNumberOfThreads (unsigned int nr_threads = 0); protected: diff --git a/features/include/pcl/features/spin_image.h b/features/include/pcl/features/spin_image.h index 79e9140d..abf4703a 100644 --- a/features/include/pcl/features/spin_image.h +++ b/features/include/pcl/features/spin_image.h @@ -68,7 +68,7 @@ namespace pcl * different than feature estimation methods that extend \ref * FeatureFromNormals, which match the normals with the search surface. * - * With the default paramters, pcl::Histogram<153> is a good choice for PointOutT. + * With the default parameters, pcl::Histogram<153> is a good choice for PointOutT. * Of course the dimension of this descriptor must change to match the number * of bins set by the parameters. * diff --git a/features/include/pcl/features/usc.h b/features/include/pcl/features/usc.h index c6598df6..de096b32 100644 --- a/features/include/pcl/features/usc.h +++ b/features/include/pcl/features/usc.h @@ -52,7 +52,7 @@ namespace pcl * - F. Tombari, S. Salti, L. Di Stefano, * "Unique Shape Context for 3D data description", * International Workshop on 3D Object Retrieval (3DOR 10) - - * in conjuction with ACM Multimedia 2010 + * in conjunction with ACM Multimedia 2010 * * The suggested PointOutT is pcl::UniqueShapeContext1960 * diff --git a/features/src/flare.cpp b/features/src/flare.cpp new file mode 100644 index 00000000..5d35b762 --- /dev/null +++ b/features/src/flare.cpp @@ -0,0 +1,49 @@ +/* +* Software License Agreement (BSD License) +* +* Point Cloud Library (PCL) - www.pointclouds.org +* Copyright (c) 2016-, Open Perception, Inc. +* +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided +* with the distribution. +* * Neither the name of the copyright holder(s) nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ +#include + + +#ifndef PCL_NO_PRECOMPILE +#include +#include +// Instantiations of specific point types +#ifdef PCL_ONLY_CORE_POINT_TYPES +PCL_INSTANTIATE_PRODUCT(FLARELocalReferenceFrameEstimation, ((pcl::PointXYZ)(pcl::PointXYZI)(pcl::PointXYZRGB)(pcl::PointXYZRGBA))((pcl::Normal))((pcl::ReferenceFrame))((float))) +#else +PCL_INSTANTIATE_PRODUCT(FLARELocalReferenceFrameEstimation, (PCL_XYZ_POINT_TYPES)(PCL_NORMAL_POINT_TYPES)((pcl::ReferenceFrame))((float))) +#endif +#endif // PCL_NO_PRECOMPILE diff --git a/common/include/pcl/ros/register_point_struct.h b/features/src/gasd.cpp similarity index 72% rename from common/include/pcl/ros/register_point_struct.h rename to features/src/gasd.cpp index 20e02311..b3b4aa27 100644 --- a/common/include/pcl/ros/register_point_struct.h +++ b/features/src/gasd.cpp @@ -2,8 +2,8 @@ * Software License Agreement (BSD License) * * Point Cloud Library (PCL) - www.pointclouds.org - * Copyright (c) 2010-2012, Willow Garage, Inc. - * Copyright (c) 2012-, Open Perception, Inc. + * Copyright (c) 2016-, Open Perception, Inc. + * Copyright (c) 2016, Voxar Labs, CIn-UFPE / DEINFO-UFRPE * * All rights reserved. * @@ -34,19 +34,14 @@ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * $Id$ - * */ -#ifndef PCL_ROS_REGISTER_POINT_STRUCT_H_ -#define PCL_ROS_REGISTER_POINT_STRUCT_H_ - -#ifdef __DEPRECATED -#warning The header is deprecated. please use \ - instead. -#endif - -#include - +#include -#endif //#ifndef PCL_ROS_REGISTER_POINT_STRUCT_H_ +#ifndef PCL_NO_PRECOMPILE +#include +#include +// Instantiations of specific point types +PCL_INSTANTIATE_PRODUCT(GASDEstimation, ((pcl::PointXYZ)(pcl::PointXYZRGB)(pcl::PointXYZRGBA)) ((pcl::GASDSignature512)(pcl::GASDSignature984)(pcl::GASDSignature7992))) +PCL_INSTANTIATE_PRODUCT(GASDColorEstimation, ((pcl::PointXYZRGB)(pcl::PointXYZRGBA)) ((pcl::GASDSignature512)(pcl::GASDSignature984)(pcl::GASDSignature7992))) +#endif // PCL_NO_PRECOMPILE diff --git a/features/src/moment_of_inertia_estimation.cpp b/features/src/moment_of_inertia_estimation.cpp index 2d174fc2..0c1c9e4b 100644 --- a/features/src/moment_of_inertia_estimation.cpp +++ b/features/src/moment_of_inertia_estimation.cpp @@ -45,11 +45,8 @@ #include // Instantiations of specific point types #ifdef PCL_ONLY_CORE_POINT_TYPES - PCL_INSTANTIATE_PRODUCT(MomentOfInertiaEstimation, ((pcl::PointXYZ))) - PCL_INSTANTIATE_PRODUCT(MomentOfInertiaEstimation, ((pcl::PointXYZI))) - PCL_INSTANTIATE_PRODUCT(MomentOfInertiaEstimation, ((pcl::PointXYZRGBA))) - PCL_INSTANTIATE_PRODUCT(MomentOfInertiaEstimation, ((pcl::PointNormal))) + PCL_INSTANTIATE(MomentOfInertiaEstimation, (pcl::PointXYZ)(pcl::PointXYZI)(pcl::PointXYZRGBA)(pcl::PointNormal)) #else - PCL_INSTANTIATE_PRODUCT(MomentOfInertiaEstimation, (PCL_XYZ_POINT_TYPES)) + PCL_INSTANTIATE(MomentOfInertiaEstimation, PCL_XYZ_POINT_TYPES) #endif #endif // PCL_NO_PRECOMPILE diff --git a/features/src/range_image_border_extractor.cpp b/features/src/range_image_border_extractor.cpp index 713606a6..e71b2a54 100644 --- a/features/src/range_image_border_extractor.cpp +++ b/features/src/range_image_border_extractor.cpp @@ -504,7 +504,7 @@ RangeImageBorderExtractor::calculateBorderDirections () if (neighbor_border_direction==NULL || index2==index) continue; - // Oposite directions? + // Opposite directions? float cos_angle = neighbor_border_direction->dot(*border_direction); if (cos_angle* clone () const = 0; + EIGEN_MAKE_ALIGNED_OPERATOR_NEW }; } diff --git a/filters/include/pcl/filters/conditional_removal.h b/filters/include/pcl/filters/conditional_removal.h index 50f8a780..3e3f3263 100644 --- a/filters/include/pcl/filters/conditional_removal.h +++ b/filters/include/pcl/filters/conditional_removal.h @@ -141,7 +141,7 @@ namespace pcl * \param op the operator to use when making the comparison * \param compare_val the constant value to compare the field value too */ - FieldComparison (std::string field_name, ComparisonOps::CompareOp op, double compare_val); + FieldComparison (const std::string &field_name, ComparisonOps::CompareOp op, double compare_val); /** \brief Copy constructor. * \param[in] src the field comparison object to copy into this @@ -204,7 +204,7 @@ namespace pcl * \param op the operator to use when making the comparison * \param compare_val the constant value to compare the component value too */ - PackedRGBComparison (std::string component_name, ComparisonOps::CompareOp op, double compare_val); + PackedRGBComparison (const std::string &component_name, ComparisonOps::CompareOp op, double compare_val); /** \brief Destructor. */ virtual ~PackedRGBComparison () {} @@ -251,7 +251,7 @@ namespace pcl * \param op the operator to use when making the comparison * \param compare_val the constant value to compare the component value too */ - PackedHSIComparison (std::string component_name, ComparisonOps::CompareOp op, double compare_val); + PackedHSIComparison (const std::string &component_name, ComparisonOps::CompareOp op, double compare_val); /** \brief Destructor. */ virtual ~PackedHSIComparison () {} @@ -622,21 +622,6 @@ namespace pcl filter_name_ = "ConditionalRemoval"; } - /** \brief a constructor that includes the condition. - * \param condition the condition that each point must satisfy to avoid - * being removed by the filter - * \param extract_removed_indices extract filtered indices from indices vector - */ - PCL_DEPRECATED ("ConditionalRemoval(ConditionBasePtr condition, bool extract_removed_indices = false) is deprecated, " - "please use the setCondition (ConditionBasePtr condition) function instead.") - ConditionalRemoval (ConditionBasePtr condition, bool extract_removed_indices = false) : - Filter::Filter (extract_removed_indices), capable_ (false), keep_organized_ (false), condition_ (), - user_filter_value_ (std::numeric_limits::quiet_NaN ()) - { - filter_name_ = "ConditionalRemoval"; - setCondition (condition); - } - /** \brief Set whether the filtered points should be kept and set to the * value given through \a setUserFilterValue (default: NaN), or removed * from the PointCloud, thus potentially breaking its organized diff --git a/filters/include/pcl/filters/convolution.h b/filters/include/pcl/filters/convolution.h index d96dea68..b5120d7e 100644 --- a/filters/include/pcl/filters/convolution.h +++ b/filters/include/pcl/filters/convolution.h @@ -65,8 +65,8 @@ namespace pcl * policies: * - Ignoring: elements at special locations are filled with zero * (default behaviour) - * - Mirroring: the missing rows or columns are obtained throug mirroring - * - Duplicating: the missing rows or columns are obtained throug + * - Mirroring: the missing rows or columns are obtained through mirroring + * - Duplicating: the missing rows or columns are obtained through * duplicating * * \author Nizar Sallem diff --git a/filters/include/pcl/filters/convolution_3d.h b/filters/include/pcl/filters/convolution_3d.h index 1931673d..ccf4d7ab 100644 --- a/filters/include/pcl/filters/convolution_3d.h +++ b/filters/include/pcl/filters/convolution_3d.h @@ -80,7 +80,7 @@ namespace pcl virtual PointOutT operator() (const std::vector& indices, const std::vector& distances) = 0; - /** \brief Must call this methode before doing any computation + /** \brief Must call this method before doing any computation * \note make sure to override this with at least * \code * bool initCompute () @@ -150,7 +150,7 @@ namespace pcl inline void setThreshold (float threshold) { threshold_ = threshold; } - /** Must call this methode before doing any computation */ + /** Must call this method before doing any computation */ bool initCompute (); virtual PointOutT diff --git a/filters/include/pcl/filters/covariance_sampling.h b/filters/include/pcl/filters/covariance_sampling.h index a3f651fc..fc315123 100644 --- a/filters/include/pcl/filters/covariance_sampling.h +++ b/filters/include/pcl/filters/covariance_sampling.h @@ -117,7 +117,7 @@ namespace pcl /** \brief Compute the condition number of the input point cloud. The condition number is the ratio between the * largest and smallest eigenvalues of the 6x6 covariance matrix of the cloud. The closer this number is to 1.0, * the more stable the cloud is for ICP registration. - * \param[in] covariance_matrix user given covariance matrix + * \param[in] covariance_matrix user given covariance matrix. Assumed to be self adjoint/symmetric. * \return the condition number */ static double diff --git a/filters/include/pcl/filters/crop_box.h b/filters/include/pcl/filters/crop_box.h index e632c639..cfec365b 100644 --- a/filters/include/pcl/filters/crop_box.h +++ b/filters/include/pcl/filters/crop_box.h @@ -186,7 +186,6 @@ namespace pcl */ void applyFilter (std::vector &indices); - private: /** \brief The minimum point of the box. */ Eigen::Vector4f min_pt_; diff --git a/filters/include/pcl/filters/fast_bilateral_omp.h b/filters/include/pcl/filters/fast_bilateral_omp.h index a93c1849..dc9b6194 100644 --- a/filters/include/pcl/filters/fast_bilateral_omp.h +++ b/filters/include/pcl/filters/fast_bilateral_omp.h @@ -68,27 +68,28 @@ namespace pcl typedef typename Filter::PointCloud PointCloud; public: - + typedef boost::shared_ptr< FastBilateralFilterOMP > Ptr; typedef boost::shared_ptr< const FastBilateralFilterOMP > ConstPtr; /** \brief Empty constructor. */ FastBilateralFilterOMP (unsigned int nr_threads = 0) - : threads_ (nr_threads) - { } + { + setNumberOfThreads(nr_threads); + } /** \brief Initialize the scheduler and set the number of threads to use. * \param nr_threads the number of hardware threads to use (0 sets the value back to automatic) */ - inline void - setNumberOfThreads (unsigned int nr_threads = 0) { threads_ = nr_threads; } + void + setNumberOfThreads (unsigned int nr_threads = 0); /** \brief Filter the input data and store the results into output. * \param[out] output the resultant point cloud */ void applyFilter (PointCloud &output); - + protected: /** \brief The number of threads the scheduler should use. */ unsigned int threads_; diff --git a/filters/include/pcl/filters/filter.h b/filters/include/pcl/filters/filter.h index 4d6c292c..ef30ed70 100644 --- a/filters/include/pcl/filters/filter.h +++ b/filters/include/pcl/filters/filter.h @@ -51,28 +51,28 @@ namespace pcl { /** \brief Removes points with x, y, or z equal to NaN * \param[in] cloud_in the input point cloud - * \param[out] cloud_out the input point cloud + * \param[out] cloud_out the output point cloud * \param[out] index the mapping (ordered): cloud_out.points[i] = cloud_in.points[index[i]] * \note The density of the point cloud is lost. * \note Can be called with cloud_in == cloud_out * \ingroup filters */ template void - removeNaNFromPointCloud (const pcl::PointCloud &cloud_in, - pcl::PointCloud &cloud_out, + removeNaNFromPointCloud (const pcl::PointCloud &cloud_in, + pcl::PointCloud &cloud_out, std::vector &index); /** \brief Removes points that have their normals invalid (i.e., equal to NaN) * \param[in] cloud_in the input point cloud - * \param[out] cloud_out the input point cloud + * \param[out] cloud_out the output point cloud * \param[out] index the mapping (ordered): cloud_out.points[i] = cloud_in.points[index[i]] * \note The density of the point cloud is lost. * \note Can be called with cloud_in == cloud_out * \ingroup filters */ template void - removeNaNNormalsFromPointCloud (const pcl::PointCloud &cloud_in, - pcl::PointCloud &cloud_out, + removeNaNNormalsFromPointCloud (const pcl::PointCloud &cloud_in, + pcl::PointCloud &cloud_out, std::vector &index); //////////////////////////////////////////////////////////////////////////////////////////// @@ -96,10 +96,10 @@ namespace pcl typedef typename PointCloud::ConstPtr PointCloudConstPtr; /** \brief Empty constructor. - * \param[in] extract_removed_indices set to true if the filtered data indices should be saved in a + * \param[in] extract_removed_indices set to true if the filtered data indices should be saved in a * separate list. Default: false. */ - Filter (bool extract_removed_indices = false) : + Filter (bool extract_removed_indices = false) : removed_indices_ (new std::vector), filter_name_ (), extract_removed_indices_ (extract_removed_indices) @@ -111,12 +111,12 @@ namespace pcl /** \brief Get the point indices being removed */ inline IndicesConstPtr const - getRemovedIndices () + getRemovedIndices () const { return (removed_indices_); } - /** \brief Get the point indices being removed + /** \brief Get the point indices being removed * \param[out] pi the resultant point indices that have been removed */ inline void @@ -168,7 +168,7 @@ namespace pcl /** \brief Set to true if we want to return the indices of the removed points. */ bool extract_removed_indices_; - /** \brief Abstract filter method. + /** \brief Abstract filter method. * * The implementation needs to set output.{points, width, height, is_dense}. * @@ -201,28 +201,28 @@ namespace pcl typedef PCLPointCloud2::Ptr PCLPointCloud2Ptr; typedef PCLPointCloud2::ConstPtr PCLPointCloud2ConstPtr; - /** \brief Empty constructor. - * \param[in] extract_removed_indices set to true if the filtered data indices should be saved in a + /** \brief Empty constructor. + * \param[in] extract_removed_indices set to true if the filtered data indices should be saved in a * separate list. Default: false. */ - Filter (bool extract_removed_indices = false) : + Filter (bool extract_removed_indices = false) : removed_indices_ (new std::vector), extract_removed_indices_ (extract_removed_indices), filter_name_ () { } - + /** \brief Empty destructor */ virtual ~Filter () {} /** \brief Get the point indices being removed */ inline IndicesConstPtr const - getRemovedIndices () + getRemovedIndices () const { return (removed_indices_); } - /** \brief Get the point indices being removed + /** \brief Get the point indices being removed * \param[out] pi the resultant point indices that have been removed */ inline void diff --git a/filters/include/pcl/filters/filter_indices.h b/filters/include/pcl/filters/filter_indices.h index 5bba9e8f..874bf1bf 100644 --- a/filters/include/pcl/filters/filter_indices.h +++ b/filters/include/pcl/filters/filter_indices.h @@ -133,7 +133,7 @@ namespace pcl * \return The value of the internal \a negative_ parameter; false = normal filter behavior (default), true = inverted behavior. */ inline bool - getNegative () + getNegative () const { return (negative_); } @@ -153,7 +153,7 @@ namespace pcl * \return The value of the internal \a keep_organized_ parameter; false = remove points (default), true = redefine points, keep structure. */ inline bool - getKeepOrganized () + getKeepOrganized () const { return (keep_organized_); } @@ -249,7 +249,7 @@ namespace pcl * \return The value of the internal \a negative_ parameter; false = normal filter behavior (default), true = inverted behavior. */ inline bool - getNegative () + getNegative () const { return (negative_); } @@ -269,7 +269,7 @@ namespace pcl * \return The value of the internal \a keep_organized_ parameter; false = remove points (default), true = redefine points, keep structure. */ inline bool - getKeepOrganized () + getKeepOrganized () const { return (keep_organized_); } diff --git a/filters/include/pcl/filters/impl/bilateral.hpp b/filters/include/pcl/filters/impl/bilateral.hpp index fe0dfb00..64687ae5 100644 --- a/filters/include/pcl/filters/impl/bilateral.hpp +++ b/filters/include/pcl/filters/impl/bilateral.hpp @@ -109,5 +109,5 @@ pcl::BilateralFilter::applyFilter (PointCloud &output) #define PCL_INSTANTIATE_BilateralFilter(T) template class PCL_EXPORTS pcl::BilateralFilter; -#endif // PCL_FILTERS_BILATERAL_H_ +#endif // PCL_FILTERS_BILATERAL_IMPL_H_ diff --git a/filters/include/pcl/filters/impl/box_clipper3D.hpp b/filters/include/pcl/filters/impl/box_clipper3D.hpp index 9f0bb24e..edcd5f5c 100644 --- a/filters/include/pcl/filters/impl/box_clipper3D.hpp +++ b/filters/include/pcl/filters/impl/box_clipper3D.hpp @@ -107,22 +107,12 @@ pcl::BoxClipper3D::transformPoint (const PointT& pointIn, PointT& pointO } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// ToDo use product on point.getVector3fMap () and transformatio_.col (i) to use the SSE advantages of eigen template bool pcl::BoxClipper3D::clipPoint3D (const PointT& point) const { - return (fabs(transformation_.data () [ 0] * point.x + - transformation_.data () [ 3] * point.y + - transformation_.data () [ 6] * point.z + - transformation_.data () [ 9]) <= 1 && - fabs(transformation_.data () [ 1] * point.x + - transformation_.data () [ 4] * point.y + - transformation_.data () [ 7] * point.z + - transformation_.data () [10]) <= 1 && - fabs(transformation_.data () [ 2] * point.x + - transformation_.data () [ 5] * point.y + - transformation_.data () [ 8] * point.z + - transformation_.data () [11]) <= 1 ); + Eigen::Vector4f point_coordinates (transformation_.matrix () + * point.getVector4fMap ()); + return (point_coordinates.array ().abs () <= 1).all (); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -130,7 +120,7 @@ pcl::BoxClipper3D::clipPoint3D (const PointT& point) const * @attention untested code */ template bool -pcl::BoxClipper3D::clipLineSegment3D (PointT& point1, PointT& point2) const +pcl::BoxClipper3D::clipLineSegment3D (PointT&, PointT&) const { /* PointT pt1, pt2; @@ -175,6 +165,7 @@ pcl::BoxClipper3D::clipLineSegment3D (PointT& point1, PointT& point2) co return true; } */ + throw std::logic_error ("Not implemented"); return false; } @@ -183,10 +174,11 @@ pcl::BoxClipper3D::clipLineSegment3D (PointT& point1, PointT& point2) co * @attention untested code */ template void -pcl::BoxClipper3D::clipPlanarPolygon3D (const std::vector >& polygon, std::vector >& clipped_polygon) const +pcl::BoxClipper3D::clipPlanarPolygon3D (const std::vector >&, std::vector >& clipped_polygon) const { // not implemented -> clip everything clipped_polygon.clear (); + throw std::logic_error ("Not implemented"); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -198,6 +190,7 @@ pcl::BoxClipper3D::clipPlanarPolygon3D (std::vector clip everything polygon.clear (); + throw std::logic_error ("Not implemented"); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -205,6 +198,7 @@ pcl::BoxClipper3D::clipPlanarPolygon3D (std::vector void pcl::BoxClipper3D::clipPointCloud3D (const pcl::PointCloud& cloud_in, std::vector& clipped, const std::vector& indices) const { + clipped.clear (); if (indices.empty ()) { clipped.reserve (cloud_in.size ()); @@ -219,4 +213,4 @@ pcl::BoxClipper3D::clipPointCloud3D (const pcl::PointCloud& clou clipped.push_back (*iIt); } } -#endif //PCL_FILTERS_IMPL_BOX_CLIPPER3D_HPP \ No newline at end of file +#endif //PCL_FILTERS_IMPL_BOX_CLIPPER3D_HPP diff --git a/filters/include/pcl/filters/impl/conditional_removal.hpp b/filters/include/pcl/filters/impl/conditional_removal.hpp index 9a20beff..25edaa68 100644 --- a/filters/include/pcl/filters/impl/conditional_removal.hpp +++ b/filters/include/pcl/filters/impl/conditional_removal.hpp @@ -47,7 +47,7 @@ ////////////////////////////////////////////////////////////////////////// template pcl::FieldComparison::FieldComparison ( - std::string field_name, ComparisonOps::CompareOp op, double compare_val) + const std::string &field_name, ComparisonOps::CompareOp op, double compare_val) : ComparisonBase () , compare_val_ (compare_val), point_data_ (NULL) { @@ -106,7 +106,7 @@ pcl::FieldComparison::evaluate (const PointT &point) const { if (!this->capable_) { - PCL_WARN ("[pcl::FieldComparison::evaluate] invalid compariosn!\n"); + PCL_WARN ("[pcl::FieldComparison::evaluate] invalid comparison!\n"); return (false); } @@ -138,7 +138,7 @@ pcl::FieldComparison::evaluate (const PointT &point) const ////////////////////////////////////////////////////////////////////////// template pcl::PackedRGBComparison::PackedRGBComparison ( - std::string component_name, ComparisonOps::CompareOp op, double compare_val) : + const std::string &component_name, ComparisonOps::CompareOp op, double compare_val) : component_name_ (component_name), component_offset_ (), compare_val_ (compare_val) { // get all the fields @@ -230,7 +230,7 @@ pcl::PackedRGBComparison::evaluate (const PointT &point) const ////////////////////////////////////////////////////////////////////////// template pcl::PackedHSIComparison::PackedHSIComparison ( - std::string component_name, ComparisonOps::CompareOp op, double compare_val) : + const std::string &component_name, ComparisonOps::CompareOp op, double compare_val) : component_name_ (component_name), component_id_ (), compare_val_ (compare_val), rgb_offset_ () { // Get all the fields @@ -287,7 +287,7 @@ pcl::PackedHSIComparison::PackedHSIComparison ( } else { - PCL_WARN ("[pcl::PackedRGBComparison::PackedRGBComparison] unrecognized component name!\n"); + PCL_WARN ("[pcl::PackedHSIComparison::PackedHSIComparison] unrecognized component name!\n"); capable_ = false; return; } @@ -527,7 +527,7 @@ pcl::TfQuadraticXYZComparison::evaluate (const PointT &point) const case pcl::ComparisonOps::EQ: return (myVal == 0); default: - PCL_WARN ("[pcl::transformableQuadricXYZComparison::evaluate] unrecognized op_!\n"); + PCL_WARN ("[pcl::TfQuadraticXYZComparison::evaluate] unrecognized op_!\n"); return (false); } } @@ -595,7 +595,7 @@ pcl::PointDataAtOffset::compare (const PointT& p, const double& val) return (pt_val > val) - (pt_val < val); } default : - PCL_WARN ("[pcl::pcl::PointDataAtOffset::compare] unknown data_type!\n"); + PCL_WARN ("[pcl::PointDataAtOffset::compare] unknown data_type!\n"); return (0); } } diff --git a/filters/include/pcl/filters/impl/covariance_sampling.hpp b/filters/include/pcl/filters/impl/covariance_sampling.hpp index a5285d6d..8c1cc57a 100644 --- a/filters/include/pcl/filters/impl/covariance_sampling.hpp +++ b/filters/include/pcl/filters/impl/covariance_sampling.hpp @@ -88,23 +88,7 @@ pcl::CovarianceSampling::computeConditionNumber () if (!computeCovarianceMatrix (covariance_matrix)) return (-1.); - Eigen::EigenSolver > eigen_solver; - eigen_solver.compute (covariance_matrix, true); - - Eigen::MatrixXcd complex_eigenvalues = eigen_solver.eigenvalues (); - - double max_ev = -std::numeric_limits::max (), - min_ev = std::numeric_limits::max (); - for (size_t i = 0; i < 6; ++i) - { - if (real (complex_eigenvalues (i, 0)) > max_ev) - max_ev = real (complex_eigenvalues (i, 0)); - - if (real (complex_eigenvalues (i, 0)) < min_ev) - min_ev = real (complex_eigenvalues (i, 0)); - } - - return (max_ev / min_ev); + return computeConditionNumber (covariance_matrix); } @@ -112,22 +96,9 @@ pcl::CovarianceSampling::computeConditionNumber () template double pcl::CovarianceSampling::computeConditionNumber (const Eigen::Matrix &covariance_matrix) { - Eigen::EigenSolver > eigen_solver; - eigen_solver.compute (covariance_matrix, true); - - Eigen::MatrixXcd complex_eigenvalues = eigen_solver.eigenvalues (); - - double max_ev = -std::numeric_limits::max (), - min_ev = std::numeric_limits::max (); - for (size_t i = 0; i < 6; ++i) - { - if (real (complex_eigenvalues (i, 0)) > max_ev) - max_ev = real (complex_eigenvalues (i, 0)); - - if (real (complex_eigenvalues (i, 0)) < min_ev) - min_ev = real (complex_eigenvalues (i, 0)); - } - + const Eigen::SelfAdjointEigenSolver > solver (covariance_matrix, Eigen::EigenvaluesOnly); + const double max_ev = solver.eigenvalues (). maxCoeff (); + const double min_ev = solver.eigenvalues (). minCoeff (); return (max_ev / min_ev); } @@ -158,31 +129,13 @@ pcl::CovarianceSampling::computeCovarianceMatrix (Eigen::Matrix template void pcl::CovarianceSampling::applyFilter (std::vector &sampled_indices) { - if (!initCompute ()) + Eigen::Matrix c_mat; + // Invokes initCompute() + if (!computeCovarianceMatrix (c_mat)) return; - //--- Part A from the paper - // Set up matrix F - Eigen::Matrix f_mat = Eigen::Matrix (6, indices_->size ()); - for (size_t p_i = 0; p_i < scaled_points_.size (); ++p_i) - { - f_mat.block<3, 1> (0, p_i) = scaled_points_[p_i].cross ( - (*input_normals_)[(*indices_)[p_i]].getNormalVector3fMap ()).template cast (); - f_mat.block<3, 1> (3, p_i) = (*input_normals_)[(*indices_)[p_i]].getNormalVector3fMap ().template cast (); - } - - // Compute the covariance matrix C and its 6 eigenvectors (initially complex, move them to a double matrix) - Eigen::Matrix c_mat (f_mat * f_mat.transpose ()); - - Eigen::EigenSolver > eigen_solver; - eigen_solver.compute (c_mat, true); - Eigen::MatrixXcd complex_eigenvectors = eigen_solver.eigenvectors (); - - Eigen::Matrix x; - for (size_t i = 0; i < 6; ++i) - for (size_t j = 0; j < 6; ++j) - x (i, j) = real (complex_eigenvectors (i, j)); - + const Eigen::SelfAdjointEigenSolver > solver (c_mat); + const Eigen::Matrix x = solver.eigenvectors (); //--- Part B from the paper /// TODO figure out how to fill the candidate_indices - see subsequent paper paragraphs diff --git a/filters/include/pcl/filters/impl/extract_indices.hpp b/filters/include/pcl/filters/impl/extract_indices.hpp index 33997cd5..ecab9162 100644 --- a/filters/include/pcl/filters/impl/extract_indices.hpp +++ b/filters/include/pcl/filters/impl/extract_indices.hpp @@ -58,7 +58,7 @@ pcl::ExtractIndices::filterDirectly (PointCloudPtr &cloud) pcl::for_each_type (pcl::detail::FieldAdder (fields)); for (int rii = 0; rii < static_cast (removed_indices_->size ()); ++rii) // rii = removed indices iterator { - int pt_index = (*removed_indices_)[rii]; + size_t pt_index = (size_t) (*removed_indices_)[rii]; if (pt_index >= input_->points.size ()) { PCL_ERROR ("[pcl::%s::filterDirectly] The index exceeds the size of the input. Do nothing.\n", @@ -91,7 +91,7 @@ pcl::ExtractIndices::applyFilter (PointCloud &output) pcl::for_each_type (pcl::detail::FieldAdder (fields)); for (int rii = 0; rii < static_cast (removed_indices_->size ()); ++rii) // rii = removed indices iterator { - int pt_index = (*removed_indices_)[rii]; + size_t pt_index = (size_t)(*removed_indices_)[rii]; if (pt_index >= input_->points.size ()) { PCL_ERROR ("[pcl::%s::applyFilter] The index exceeds the size of the input. Do nothing.\n", diff --git a/filters/include/pcl/filters/impl/fast_bilateral_omp.hpp b/filters/include/pcl/filters/impl/fast_bilateral_omp.hpp index ee724184..a1bdcccf 100644 --- a/filters/include/pcl/filters/impl/fast_bilateral_omp.hpp +++ b/filters/include/pcl/filters/impl/fast_bilateral_omp.hpp @@ -45,6 +45,20 @@ #include #include +////////////////////////////////////////////////////////////////////////////////////////////// +template void +pcl::FastBilateralFilterOMP::setNumberOfThreads (unsigned int nr_threads) +{ + if (nr_threads == 0) +#ifdef _OPENMP + threads_ = omp_get_num_procs(); +#else + threads_ = 1; +#endif + else + threads_ = nr_threads; +} + ////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::FastBilateralFilterOMP::applyFilter (PointCloud &output) diff --git a/filters/include/pcl/filters/impl/filter.hpp b/filters/include/pcl/filters/impl/filter.hpp index 90c65260..91506d9a 100644 --- a/filters/include/pcl/filters/impl/filter.hpp +++ b/filters/include/pcl/filters/impl/filter.hpp @@ -43,7 +43,7 @@ ////////////////////////////////////////////////////////////////////////// template void -pcl::removeNaNFromPointCloud (const pcl::PointCloud &cloud_in, +pcl::removeNaNFromPointCloud (const pcl::PointCloud &cloud_in, pcl::PointCloud &cloud_out, std::vector &index) { @@ -52,6 +52,8 @@ pcl::removeNaNFromPointCloud (const pcl::PointCloud &cloud_in, { cloud_out.header = cloud_in.header; cloud_out.points.resize (cloud_in.points.size ()); + cloud_out.sensor_origin_ = cloud_in.sensor_origin_; + cloud_out.sensor_orientation_ = cloud_in.sensor_orientation_; } // Reserve enough space for the indices index.resize (cloud_in.points.size ()); @@ -69,8 +71,8 @@ pcl::removeNaNFromPointCloud (const pcl::PointCloud &cloud_in, { for (size_t i = 0; i < cloud_in.points.size (); ++i) { - if (!pcl_isfinite (cloud_in.points[i].x) || - !pcl_isfinite (cloud_in.points[i].y) || + if (!pcl_isfinite (cloud_in.points[i].x) || + !pcl_isfinite (cloud_in.points[i].y) || !pcl_isfinite (cloud_in.points[i].z)) continue; cloud_out.points[j] = cloud_in.points[i]; @@ -94,7 +96,7 @@ pcl::removeNaNFromPointCloud (const pcl::PointCloud &cloud_in, ////////////////////////////////////////////////////////////////////////// template void -pcl::removeNaNNormalsFromPointCloud (const pcl::PointCloud &cloud_in, +pcl::removeNaNNormalsFromPointCloud (const pcl::PointCloud &cloud_in, pcl::PointCloud &cloud_out, std::vector &index) { @@ -103,6 +105,8 @@ pcl::removeNaNNormalsFromPointCloud (const pcl::PointCloud &cloud_in, { cloud_out.header = cloud_in.header; cloud_out.points.resize (cloud_in.points.size ()); + cloud_out.sensor_origin_ = cloud_in.sensor_origin_; + cloud_out.sensor_orientation_ = cloud_in.sensor_orientation_; } // Reserve enough space for the indices index.resize (cloud_in.points.size ()); @@ -110,8 +114,8 @@ pcl::removeNaNNormalsFromPointCloud (const pcl::PointCloud &cloud_in, for (size_t i = 0; i < cloud_in.points.size (); ++i) { - if (!pcl_isfinite (cloud_in.points[i].normal_x) || - !pcl_isfinite (cloud_in.points[i].normal_y) || + if (!pcl_isfinite (cloud_in.points[i].normal_x) || + !pcl_isfinite (cloud_in.points[i].normal_y) || !pcl_isfinite (cloud_in.points[i].normal_z)) continue; cloud_out.points[j] = cloud_in.points[i]; diff --git a/filters/include/pcl/filters/impl/frustum_culling.hpp b/filters/include/pcl/filters/impl/frustum_culling.hpp index 7470b794..81fe09f2 100644 --- a/filters/include/pcl/filters/impl/frustum_culling.hpp +++ b/filters/include/pcl/filters/impl/frustum_culling.hpp @@ -83,7 +83,7 @@ pcl::FrustumCulling::applyFilter (std::vector &indices) Eigen::Vector4f pl_l; // left plane Eigen::Vector3f view = camera_pose_.block (0, 0, 3, 1); // view vector for the camera - first column of the rotation matrix - Eigen::Vector3f up = camera_pose_.block (0, 1, 3, 1); // up vector for the camera - second column of the rotation matix + Eigen::Vector3f up = camera_pose_.block (0, 1, 3, 1); // up vector for the camera - second column of the rotation matrix Eigen::Vector3f right = camera_pose_.block (0, 2, 3, 1); // right vector for the camera - third column of the rotation matrix Eigen::Vector3f T = camera_pose_.block (0, 3, 3, 1); // The (X, Y, Z) position of the camera w.r.t origin diff --git a/filters/include/pcl/filters/impl/project_inliers.hpp b/filters/include/pcl/filters/impl/project_inliers.hpp index 8e0473de..a22304e7 100644 --- a/filters/include/pcl/filters/impl/project_inliers.hpp +++ b/filters/include/pcl/filters/impl/project_inliers.hpp @@ -61,7 +61,7 @@ pcl::ProjectInliers::applyFilter (PointCloud &output) // Initialize the Sample Consensus model and set its parameters if (!initSACModel (model_type_)) { - PCL_ERROR ("[pcl::%s::segment] Error initializing the SAC model!\n", getClassName ().c_str ()); + PCL_ERROR ("[pcl::%s::applyFilter] Error initializing the SAC model!\n", getClassName ().c_str ()); output.width = output.height = 0; output.points.clear (); return; diff --git a/filters/include/pcl/filters/impl/random_sample.hpp b/filters/include/pcl/filters/impl/random_sample.hpp index 0aea70fc..dd4b4e99 100644 --- a/filters/include/pcl/filters/impl/random_sample.hpp +++ b/filters/include/pcl/filters/impl/random_sample.hpp @@ -92,9 +92,8 @@ template void pcl::RandomSample::applyFilter (std::vector &indices) { - unsigned N = static_cast (indices_->size ()); - - unsigned int sample_size = negative_ ? N - sample_ : sample_; + size_t N = indices_->size (); + size_t sample_size = negative_ ? N - sample_ : sample_; // If sample size is 0 or if the sample size is greater then input cloud size // then return all indices if (sample_size >= N) @@ -105,48 +104,44 @@ pcl::RandomSample::applyFilter (std::vector &indices) else { // Resize output indices to sample size - indices.resize (static_cast (sample_size)); + indices.resize (sample_size); if (extract_removed_indices_) - removed_indices_->resize (static_cast (N - sample_size)); + removed_indices_->resize (N - sample_size); // Set random seed so derived indices are the same each time the filter runs std::srand (seed_); - // Algorithm A - unsigned top = N - sample_size; - unsigned i = 0; - unsigned index = 0; + // Algorithm S + size_t i = 0; + size_t index = 0; std::vector added; if (extract_removed_indices_) added.resize (indices_->size (), false); - for (size_t n = sample_size; n >= 2; n--) + size_t n = sample_size; + while (n > 0) { - float V = unifRand (); - unsigned S = 0; - float quot = static_cast (top) / static_cast (N); - while (quot > V) + // Step 1: [Generate U.] Generate a random variate U that is uniformly distributed between 0 and 1. + const float U = unifRand (); + // Step 2: [Test.] If N * U > n, go to Step 4. + if ((N * U) <= n) { - S++; - top--; - N--; - quot = quot * static_cast (top) / static_cast (N); + // Step 3: [Select.] Select the next record in the file for the sample, and set n : = n - 1. + if (extract_removed_indices_) + added[index] = true; + indices[i++] = (*indices_)[index]; + --n; } - index += S; - if (extract_removed_indices_) - added[index] = true; - indices[i++] = (*indices_)[index++]; - N--; + // Step 4: [Don't select.] Skip over the next record (do not include it in the sample). + // Set N : = N - 1. + --N; + ++index; + // If n > 0, then return to Step 1; otherwise, the sample is complete and the algorithm terminates. } - index += N * static_cast (unifRand ()); - if (extract_removed_indices_) - added[index] = true; - indices[i++] = (*indices_)[index++]; - // Now populate removed_indices_ appropriately if (extract_removed_indices_) { - unsigned ri = 0; + size_t ri = 0; for (size_t i = 0; i < added.size (); i++) { if (!added[i]) diff --git a/filters/include/pcl/filters/impl/uniform_sampling.hpp b/filters/include/pcl/filters/impl/uniform_sampling.hpp index d076fb47..d8a5b219 100644 --- a/filters/include/pcl/filters/impl/uniform_sampling.hpp +++ b/filters/include/pcl/filters/impl/uniform_sampling.hpp @@ -79,15 +79,24 @@ pcl::UniformSampling::applyFilter (PointCloud &output) // Set up the division multiplier divb_mul_ = Eigen::Vector4i (1, div_b_[0], div_b_[0] * div_b_[1], 0); + Filter::removed_indices_->clear(); // First pass: build a set of leaves with the point index closest to the leaf center for (size_t cp = 0; cp < indices_->size (); ++cp) { if (!input_->is_dense) + { // Check if the point is invalid if (!pcl_isfinite (input_->points[(*indices_)[cp]].x) || !pcl_isfinite (input_->points[(*indices_)[cp]].y) || !pcl_isfinite (input_->points[(*indices_)[cp]].z)) + { + if (Filter::extract_removed_indices_) + { + Filter::removed_indices_->push_back ((*indices_)[cp]); + } continue; + } + } Eigen::Vector4i ijk = Eigen::Vector4i::Zero (); ijk[0] = static_cast (floor (input_->points[(*indices_)[cp]].x * inverse_leaf_size_[0])); @@ -110,7 +119,14 @@ pcl::UniformSampling::applyFilter (PointCloud &output) // If current point is closer, copy its index instead if (diff_cur < diff_prev) + { + if (Filter::extract_removed_indices_) + { + Filter::removed_indices_->push_back (leaf.idx); + } + leaf.idx = (*indices_)[cp]; + } } // Second pass: go over all leaves and copy data diff --git a/filters/include/pcl/filters/impl/voxel_grid.hpp b/filters/include/pcl/filters/impl/voxel_grid.hpp index 9f2db7a9..4240308a 100644 --- a/filters/include/pcl/filters/impl/voxel_grid.hpp +++ b/filters/include/pcl/filters/impl/voxel_grid.hpp @@ -342,7 +342,7 @@ pcl::VoxelGrid::applyFilter (PointCloud &output) std::sort (index_vector.begin (), index_vector.end (), std::less ()); // Third pass: count output cells - // we need to skip all the same, adjacenent idx values + // we need to skip all the same, adjacent idx values unsigned int total = 0; unsigned int index = 0; // first_and_last_indices_vector[i] represents the index in index_vector of the first point in diff --git a/filters/include/pcl/filters/model_outlier_removal.h b/filters/include/pcl/filters/model_outlier_removal.h index 600d90de..5587d9f1 100644 --- a/filters/include/pcl/filters/model_outlier_removal.h +++ b/filters/include/pcl/filters/model_outlier_removal.h @@ -228,7 +228,7 @@ namespace pcl /** \brief The model used to calculate distances */ SampleConsensusModelPtr model_; - /** \brief The threshold used to seperate outliers (removed_indices) from inliers (indices) */ + /** \brief The threshold used to separate outliers (removed_indices) from inliers (indices) */ float thresh_; /** \brief The model coefficients */ diff --git a/filters/include/pcl/filters/passthrough.h b/filters/include/pcl/filters/passthrough.h index 077a9f9c..f1163c85 100644 --- a/filters/include/pcl/filters/passthrough.h +++ b/filters/include/pcl/filters/passthrough.h @@ -117,7 +117,7 @@ namespace pcl * \return The name of the field that will be used for filtering. */ inline std::string const - getFilterFieldName () + getFilterFieldName () const { return (filter_field_name_); } @@ -139,7 +139,7 @@ namespace pcl * \param[out] limit_max The maximum allowed field value (default = FLT_MAX). */ inline void - getFilterLimits (float &limit_min, float &limit_max) + getFilterLimits (float &limit_min, float &limit_max) const { limit_min = filter_limit_min_; limit_max = filter_limit_max_; @@ -156,22 +156,22 @@ namespace pcl negative_ = limit_negative; } - /** \brief Get whether the data outside the interval (min/max) is to be returned (true) or inside (false). + /** \brief Get whether the data outside the interval (min/max) is to be returned (true) or inside (false). * \warning This method will be removed in the future. Use getNegative() instead. * \param[out] limit_negative true if data \b outside the interval [min; max] is to be returned, false otherwise */ inline void - getFilterLimitsNegative (bool &limit_negative) + getFilterLimitsNegative (bool &limit_negative) const { limit_negative = negative_; } - /** \brief Get whether the data outside the interval (min/max) is to be returned (true) or inside (false). + /** \brief Get whether the data outside the interval (min/max) is to be returned (true) or inside (false). * \warning This method will be removed in the future. Use getNegative() instead. * \return true if data \b outside the interval [min; max] is to be returned, false otherwise */ inline bool - getFilterLimitsNegative () + getFilterLimitsNegative () const { return (negative_); } @@ -252,7 +252,7 @@ namespace pcl * structure. By default, points are removed. * * \param[in] val set to true whether the filtered points should be kept and - * set to a given user value (default: NaN) + * set to a user given value (default: NaN) */ inline void setKeepOrganized (bool val) @@ -262,7 +262,7 @@ namespace pcl /** \brief Obtain the value of the internal \a keep_organized_ parameter. */ inline bool - getKeepOrganized () + getKeepOrganized () const { return (keep_organized_); } @@ -290,7 +290,7 @@ namespace pcl /** \brief Get the name of the field used for filtering. */ inline std::string const - getFilterFieldName () + getFilterFieldName () const { return (filter_field_name_); } @@ -306,12 +306,12 @@ namespace pcl filter_limit_max_ = limit_max; } - /** \brief Get the field filter limits (min/max) set by the user. The default values are -FLT_MAX, FLT_MAX. + /** \brief Get the field filter limits (min/max) set by the user. The default values are -FLT_MAX, FLT_MAX. * \param[out] limit_min the minimum allowed field value * \param[out] limit_max the maximum allowed field value */ inline void - getFilterLimits (double &limit_min, double &limit_max) + getFilterLimits (double &limit_min, double &limit_max) const { limit_min = filter_limit_min_; limit_max = filter_limit_max_; @@ -327,20 +327,20 @@ namespace pcl filter_limit_negative_ = limit_negative; } - /** \brief Get whether the data outside the interval (min/max) is to be returned (true) or inside (false). + /** \brief Get whether the data outside the interval (min/max) is to be returned (true) or inside (false). * \param[out] limit_negative true if data \b outside the interval [min; max] is to be returned, false otherwise */ inline void - getFilterLimitsNegative (bool &limit_negative) + getFilterLimitsNegative (bool &limit_negative) const { limit_negative = filter_limit_negative_; } - /** \brief Get whether the data outside the interval (min/max) is to be returned (true) or inside (false). + /** \brief Get whether the data outside the interval (min/max) is to be returned (true) or inside (false). * \return true if data \b outside the interval [min; max] is to be returned, false otherwise */ inline bool - getFilterLimitsNegative () + getFilterLimitsNegative () const { return (filter_limit_negative_); } @@ -351,12 +351,12 @@ namespace pcl private: /** \brief Keep the structure of the data organized, by setting the - * filtered points to the a user given value (NaN by default). + * filtered points to a user given value (NaN by default). */ bool keep_organized_; /** \brief User given value to be set to any filtered point. Casted to - * the correct field type. + * the correct field type. */ float user_filter_value_; diff --git a/filters/include/pcl/filters/random_sample.h b/filters/include/pcl/filters/random_sample.h index e2b0fb61..65b2e2a9 100644 --- a/filters/include/pcl/filters/random_sample.h +++ b/filters/include/pcl/filters/random_sample.h @@ -232,7 +232,6 @@ namespace pcl unifRand () { return (static_cast (rand () / double (RAND_MAX))); - //return (((214013 * seed_ + 2531011) >> 16) & 0x7FFF); } }; } diff --git a/filters/include/pcl/filters/sampling_surface_normal.h b/filters/include/pcl/filters/sampling_surface_normal.h index cdffff7f..dd8f6eb2 100644 --- a/filters/include/pcl/filters/sampling_surface_normal.h +++ b/filters/include/pcl/filters/sampling_surface_normal.h @@ -144,7 +144,7 @@ namespace pcl private: - /** \brief @b CompareDim is a comparator object for sorting across a specific dimenstion (i,.e X, Y or Z) + /** \brief @b CompareDim is a comparator object for sorting across a specific dimension (i,.e X, Y or Z) */ struct CompareDim { diff --git a/filters/include/pcl/filters/statistical_outlier_removal.h b/filters/include/pcl/filters/statistical_outlier_removal.h index 2aeb4153..3dcad2df 100644 --- a/filters/include/pcl/filters/statistical_outlier_removal.h +++ b/filters/include/pcl/filters/statistical_outlier_removal.h @@ -198,13 +198,13 @@ namespace pcl * \ingroup filters */ template<> - class PCL_EXPORTS StatisticalOutlierRemoval : public Filter + class PCL_EXPORTS StatisticalOutlierRemoval : public FilterIndices { - using Filter::filter_name_; - using Filter::getClassName; + using FilterIndices::filter_name_; + using FilterIndices::getClassName; - using Filter::removed_indices_; - using Filter::extract_removed_indices_; + using FilterIndices::removed_indices_; + using FilterIndices::extract_removed_indices_; typedef pcl::search::Search KdTree; typedef pcl::search::Search::Ptr KdTreePtr; @@ -216,8 +216,8 @@ namespace pcl public: /** \brief Empty constructor. */ StatisticalOutlierRemoval (bool extract_removed_indices = false) : - Filter::Filter (extract_removed_indices), mean_k_ (2), - std_mul_ (0.0), tree_ (), negative_ (false) + FilterIndices::FilterIndices (extract_removed_indices), mean_k_ (2), + std_mul_ (0.0), tree_ () { filter_name_ = "StatisticalOutlierRemoval"; } @@ -257,25 +257,6 @@ namespace pcl return (std_mul_); } - /** \brief Set whether the indices should be returned, or all points \e except the indices. - * \param negative true if all points \e except the input indices will be returned, false otherwise - */ - inline void - setNegative (bool negative) - { - negative_ = negative; - } - - /** \brief Get the value of the internal #negative_ parameter. If - * true, all points \e except the input indices will be returned. - * \return The value of the "negative" flag - */ - inline bool - getNegative () - { - return (negative_); - } - protected: /** \brief The number of points to use for mean distance estimation. */ int mean_k_; @@ -288,11 +269,19 @@ namespace pcl /** \brief A pointer to the spatial search object. */ KdTreePtr tree_; - /** \brief If true, the outliers will be returned instead of the inliers (default: false). */ - bool negative_; + virtual void + applyFilter (std::vector &indices); - void + virtual void applyFilter (PCLPointCloud2 &output); + + /** + * \brief Compute the statistical values used in both applyFilter methods. + * + * This method tries to avoid duplicate code. + */ + virtual void + generateStatistics (double& mean, double& variance, double& stddev, std::vector& distances); }; } diff --git a/filters/include/pcl/filters/uniform_sampling.h b/filters/include/pcl/filters/uniform_sampling.h index 8be09c71..7e14ebde 100644 --- a/filters/include/pcl/filters/uniform_sampling.h +++ b/filters/include/pcl/filters/uniform_sampling.h @@ -72,7 +72,8 @@ namespace pcl typedef boost::shared_ptr > ConstPtr; /** \brief Empty constructor. */ - UniformSampling () : + UniformSampling (bool extract_removed_indices = false) : + Filter(extract_removed_indices), leaves_ (), leaf_size_ (Eigen::Vector4f::Zero ()), inverse_leaf_size_ (Eigen::Vector4f::Zero ()), diff --git a/filters/include/pcl/filters/voxel_grid.h b/filters/include/pcl/filters/voxel_grid.h index 5b3f03f0..2e5230e0 100644 --- a/filters/include/pcl/filters/voxel_grid.h +++ b/filters/include/pcl/filters/voxel_grid.h @@ -51,14 +51,14 @@ namespace pcl * \param[in] x_idx the index of the X channel * \param[in] y_idx the index of the Y channel * \param[in] z_idx the index of the Z channel - * \param[out] min_pt the minimum data point + * \param[out] min_pt the minimum data point * \param[out] max_pt the maximum data point */ - PCL_EXPORTS void + PCL_EXPORTS void getMinMax3D (const pcl::PCLPointCloud2ConstPtr &cloud, int x_idx, int y_idx, int z_idx, Eigen::Vector4f &min_pt, Eigen::Vector4f &max_pt); - /** \brief Obtain the maximum and minimum points in 3D from a given point cloud. + /** \brief Obtain the maximum and minimum points in 3D from a given point cloud. * \note Performs internal data filtering as well. * \param[in] cloud the pointer to a pcl::PCLPointCloud2 dataset * \param[in] x_idx the index of the X channel @@ -67,14 +67,14 @@ namespace pcl * \param[in] distance_field_name the name of the dimension to filter data along to * \param[in] min_distance the minimum acceptable value in \a distance_field_name data * \param[in] max_distance the maximum acceptable value in \a distance_field_name data - * \param[out] min_pt the minimum data point + * \param[out] min_pt the minimum data point * \param[out] max_pt the maximum data point * \param[in] limit_negative \b false if data \b inside of the [min_distance; max_distance] interval should be * considered, \b true otherwise. */ - PCL_EXPORTS void + PCL_EXPORTS void getMinMax3D (const pcl::PCLPointCloud2ConstPtr &cloud, int x_idx, int y_idx, int z_idx, - const std::string &distance_field_name, float min_distance, float max_distance, + const std::string &distance_field_name, float min_distance, float max_distance, Eigen::Vector4f &min_pt, Eigen::Vector4f &max_pt, bool limit_negative = false); /** \brief Get the relative cell indices of the "upper half" 13 neighbors. @@ -88,9 +88,9 @@ namespace pcl int idx = 0; // 0 - 8 - for (int i = -1; i < 2; i++) + for (int i = -1; i < 2; i++) { - for (int j = -1; j < 2; j++) + for (int j = -1; j < 2; j++) { relative_coordinates (0, idx) = i; relative_coordinates (1, idx) = j; @@ -99,7 +99,7 @@ namespace pcl } } // 9 - 11 - for (int i = -1; i < 2; i++) + for (int i = -1; i < 2; i++) { relative_coordinates (0, idx) = i; relative_coordinates (1, idx) = -1; @@ -139,8 +139,8 @@ namespace pcl * \param[in] limit_negative if set to true, then all points outside of the interval (min_distance;max_distace) are considered * \ingroup filters */ - template void - getMinMax3D (const typename pcl::PointCloud::ConstPtr &cloud, + template void + getMinMax3D (const typename pcl::PointCloud::ConstPtr &cloud, const std::string &distance_field_name, float min_distance, float max_distance, Eigen::Vector4f &min_pt, Eigen::Vector4f &max_pt, bool limit_negative = false); @@ -156,8 +156,8 @@ namespace pcl * \param[in] limit_negative if set to true, then all points outside of the interval (min_distance;max_distace) are considered * \ingroup filters */ - template void - getMinMax3D (const typename pcl::PointCloud::ConstPtr &cloud, + template void + getMinMax3D (const typename pcl::PointCloud::ConstPtr &cloud, const std::vector &indices, const std::string &distance_field_name, float min_distance, float max_distance, Eigen::Vector4f &min_pt, Eigen::Vector4f &max_pt, bool limit_negative = false); @@ -186,24 +186,25 @@ namespace pcl typedef typename Filter::PointCloud PointCloud; typedef typename PointCloud::Ptr PointCloudPtr; typedef typename PointCloud::ConstPtr PointCloudConstPtr; + + public: + typedef boost::shared_ptr< VoxelGrid > Ptr; typedef boost::shared_ptr< const VoxelGrid > ConstPtr; - - public: /** \brief Empty constructor. */ - VoxelGrid () : + VoxelGrid () : leaf_size_ (Eigen::Vector4f::Zero ()), inverse_leaf_size_ (Eigen::Array4f::Zero ()), - downsample_all_data_ (true), + downsample_all_data_ (true), save_leaf_layout_ (false), leaf_layout_ (), min_b_ (Eigen::Vector4i::Zero ()), max_b_ (Eigen::Vector4i::Zero ()), div_b_ (Eigen::Vector4i::Zero ()), divb_mul_ (Eigen::Vector4i::Zero ()), - filter_field_name_ (""), - filter_limit_min_ (-FLT_MAX), + filter_field_name_ (""), + filter_limit_min_ (-FLT_MAX), filter_limit_max_ (FLT_MAX), filter_limit_negative_ (false), min_points_per_voxel_ (0) @@ -219,10 +220,10 @@ namespace pcl /** \brief Set the voxel grid leaf size. * \param[in] leaf_size the voxel grid leaf size */ - inline void - setLeafSize (const Eigen::Vector4f &leaf_size) - { - leaf_size_ = leaf_size; + inline void + setLeafSize (const Eigen::Vector4f &leaf_size) + { + leaf_size_ = leaf_size; // Avoid division errors if (leaf_size_[3] == 0) leaf_size_[3] = 1; @@ -247,79 +248,79 @@ namespace pcl } /** \brief Get the voxel grid leaf size. */ - inline Eigen::Vector3f - getLeafSize () { return (leaf_size_.head<3> ()); } + inline Eigen::Vector3f + getLeafSize () const { return (leaf_size_.head<3> ()); } /** \brief Set to true if all fields need to be downsampled, or false if just XYZ. * \param[in] downsample the new value (true/false) */ - inline void + inline void setDownsampleAllData (bool downsample) { downsample_all_data_ = downsample; } /** \brief Get the state of the internal downsampling parameter (true if - * all fields need to be downsampled, false if just XYZ). + * all fields need to be downsampled, false if just XYZ). */ - inline bool - getDownsampleAllData () { return (downsample_all_data_); } + inline bool + getDownsampleAllData () const { return (downsample_all_data_); } /** \brief Set the minimum number of points required for a voxel to be used. * \param[in] min_points_per_voxel the minimum number of points for required for a voxel to be used */ - inline void + inline void setMinimumPointsNumberPerVoxel (unsigned int min_points_per_voxel) { min_points_per_voxel_ = min_points_per_voxel; } /** \brief Return the minimum number of points required for a voxel to be used. */ inline unsigned int - getMinimumPointsNumberPerVoxel () { return min_points_per_voxel_; } + getMinimumPointsNumberPerVoxel () const { return min_points_per_voxel_; } /** \brief Set to true if leaf layout information needs to be saved for later access. * \param[in] save_leaf_layout the new value (true/false) */ - inline void + inline void setSaveLeafLayout (bool save_leaf_layout) { save_leaf_layout_ = save_leaf_layout; } /** \brief Returns true if leaf layout information will to be saved for later access. */ - inline bool - getSaveLeafLayout () { return (save_leaf_layout_); } + inline bool + getSaveLeafLayout () const { return (save_leaf_layout_); } /** \brief Get the minimum coordinates of the bounding box (after - * filtering is performed). + * filtering is performed). */ - inline Eigen::Vector3i - getMinBoxCoordinates () { return (min_b_.head<3> ()); } + inline Eigen::Vector3i + getMinBoxCoordinates () const { return (min_b_.head<3> ()); } /** \brief Get the minimum coordinates of the bounding box (after - * filtering is performed). + * filtering is performed). */ - inline Eigen::Vector3i - getMaxBoxCoordinates () { return (max_b_.head<3> ()); } + inline Eigen::Vector3i + getMaxBoxCoordinates () const { return (max_b_.head<3> ()); } /** \brief Get the number of divisions along all 3 axes (after filtering - * is performed). + * is performed). */ - inline Eigen::Vector3i - getNrDivisions () { return (div_b_.head<3> ()); } + inline Eigen::Vector3i + getNrDivisions () const { return (div_b_.head<3> ()); } /** \brief Get the multipliers to be applied to the grid coordinates in - * order to find the centroid index (after filtering is performed). + * order to find the centroid index (after filtering is performed). */ - inline Eigen::Vector3i - getDivisionMultiplier () { return (divb_mul_.head<3> ()); } + inline Eigen::Vector3i + getDivisionMultiplier () const { return (divb_mul_.head<3> ()); } /** \brief Returns the index in the resulting downsampled cloud of the specified point. * - * \note for efficiency, user must make sure that the saving of the leaf layout is enabled and filtering + * \note for efficiency, user must make sure that the saving of the leaf layout is enabled and filtering * performed, and that the point is inside the grid, to avoid invalid access (or use * getGridCoordinates+getCentroidIndexAt) * * \param[in] p the point to get the index at */ - inline int - getCentroidIndex (const PointT &p) + inline int + getCentroidIndex (const PointT &p) const { - return (leaf_layout_.at ((Eigen::Vector4i (static_cast (floor (p.x * inverse_leaf_size_[0])), - static_cast (floor (p.y * inverse_leaf_size_[1])), + return (leaf_layout_.at ((Eigen::Vector4i (static_cast (floor (p.x * inverse_leaf_size_[0])), + static_cast (floor (p.y * inverse_leaf_size_[1])), static_cast (floor (p.z * inverse_leaf_size_[2])), 0) - min_b_).dot (divb_mul_))); } @@ -329,11 +330,11 @@ namespace pcl * \param[in] relative_coordinates matrix with the columns being the coordinates of the requested cells, relative to the reference point's cell * \note for efficiency, user must make sure that the saving of the leaf layout is enabled and filtering performed */ - inline std::vector - getNeighborCentroidIndices (const PointT &reference_point, const Eigen::MatrixXi &relative_coordinates) + inline std::vector + getNeighborCentroidIndices (const PointT &reference_point, const Eigen::MatrixXi &relative_coordinates) const { - Eigen::Vector4i ijk (static_cast (floor (reference_point.x * inverse_leaf_size_[0])), - static_cast (floor (reference_point.y * inverse_leaf_size_[1])), + Eigen::Vector4i ijk (static_cast (floor (reference_point.x * inverse_leaf_size_[0])), + static_cast (floor (reference_point.y * inverse_leaf_size_[1])), static_cast (floor (reference_point.z * inverse_leaf_size_[2])), 0); Eigen::Array4i diff2min = min_b_ - ijk; Eigen::Array4i diff2max = max_b_ - ijk; @@ -353,27 +354,27 @@ namespace pcl /** \brief Returns the layout of the leafs for fast access to cells relative to current position. * \note position at (i-min_x) + (j-min_y)*div_x + (k-min_z)*div_x*div_y holds the index of the element at coordinates (i,j,k) in the grid (-1 if empty) */ - inline std::vector - getLeafLayout () { return (leaf_layout_); } + inline std::vector + getLeafLayout () const { return (leaf_layout_); } - /** \brief Returns the corresponding (i,j,k) coordinates in the grid of point (x,y,z). + /** \brief Returns the corresponding (i,j,k) coordinates in the grid of point (x,y,z). * \param[in] x the X point coordinate to get the (i, j, k) index at * \param[in] y the Y point coordinate to get the (i, j, k) index at * \param[in] z the Z point coordinate to get the (i, j, k) index at */ - inline Eigen::Vector3i - getGridCoordinates (float x, float y, float z) - { - return (Eigen::Vector3i (static_cast (floor (x * inverse_leaf_size_[0])), - static_cast (floor (y * inverse_leaf_size_[1])), - static_cast (floor (z * inverse_leaf_size_[2])))); + inline Eigen::Vector3i + getGridCoordinates (float x, float y, float z) const + { + return (Eigen::Vector3i (static_cast (floor (x * inverse_leaf_size_[0])), + static_cast (floor (y * inverse_leaf_size_[1])), + static_cast (floor (z * inverse_leaf_size_[2])))); } /** \brief Returns the index in the downsampled cloud corresponding to a given set of coordinates. * \param[in] ijk the coordinates (i,j,k) in the grid (-1 if empty) */ - inline int - getCentroidIndexAt (const Eigen::Vector3i &ijk) + inline int + getCentroidIndexAt (const Eigen::Vector3i &ijk) const { int idx = ((Eigen::Vector4i() << ijk, 0).finished() - min_b_).dot (divb_mul_); if (idx < 0 || idx >= static_cast (leaf_layout_.size ())) // this checks also if leaf_layout_.size () == 0 i.e. everything was computed as needed @@ -397,7 +398,7 @@ namespace pcl /** \brief Get the name of the field used for filtering. */ inline std::string const - getFilterFieldName () + getFilterFieldName () const { return (filter_field_name_); } @@ -413,12 +414,12 @@ namespace pcl filter_limit_max_ = limit_max; } - /** \brief Get the field filter limits (min/max) set by the user. The default values are -FLT_MAX, FLT_MAX. + /** \brief Get the field filter limits (min/max) set by the user. The default values are -FLT_MAX, FLT_MAX. * \param[out] limit_min the minimum allowed field value * \param[out] limit_max the maximum allowed field value */ inline void - getFilterLimits (double &limit_min, double &limit_max) + getFilterLimits (double &limit_min, double &limit_max) const { limit_min = filter_limit_min_; limit_max = filter_limit_max_; @@ -434,20 +435,20 @@ namespace pcl filter_limit_negative_ = limit_negative; } - /** \brief Get whether the data outside the interval (min/max) is to be returned (true) or inside (false). + /** \brief Get whether the data outside the interval (min/max) is to be returned (true) or inside (false). * \param[out] limit_negative true if data \b outside the interval [min; max] is to be returned, false otherwise */ inline void - getFilterLimitsNegative (bool &limit_negative) + getFilterLimitsNegative (bool &limit_negative) const { limit_negative = filter_limit_negative_; } - /** \brief Get whether the data outside the interval (min/max) is to be returned (true) or inside (false). + /** \brief Get whether the data outside the interval (min/max) is to be returned (true) or inside (false). * \return true if data \b outside the interval [min; max] is to be returned, false otherwise */ inline bool - getFilterLimitsNegative () + getFilterLimitsNegative () const { return (filter_limit_negative_); } @@ -456,7 +457,7 @@ namespace pcl /** \brief The size of a leaf. */ Eigen::Vector4f leaf_size_; - /** \brief Internal leaf sizes stored as 1/leaf_size_ for efficiency reasons. */ + /** \brief Internal leaf sizes stored as 1/leaf_size_ for efficiency reasons. */ Eigen::Array4f inverse_leaf_size_; /** \brief Set to true if all fields need to be downsampled, or false if just XYZ. */ @@ -491,7 +492,7 @@ namespace pcl /** \brief Downsample a Point Cloud using a voxelized grid approach * \param[out] output the resultant point cloud message */ - void + void applyFilter (PointCloud &output); }; @@ -519,18 +520,18 @@ namespace pcl public: /** \brief Empty constructor. */ - VoxelGrid () : + VoxelGrid () : leaf_size_ (Eigen::Vector4f::Zero ()), inverse_leaf_size_ (Eigen::Array4f::Zero ()), - downsample_all_data_ (true), + downsample_all_data_ (true), save_leaf_layout_ (false), leaf_layout_ (), min_b_ (Eigen::Vector4i::Zero ()), max_b_ (Eigen::Vector4i::Zero ()), div_b_ (Eigen::Vector4i::Zero ()), divb_mul_ (Eigen::Vector4i::Zero ()), - filter_field_name_ (""), - filter_limit_min_ (-FLT_MAX), + filter_field_name_ (""), + filter_limit_min_ (-FLT_MAX), filter_limit_max_ (FLT_MAX), filter_limit_negative_ (false), min_points_per_voxel_ (0) @@ -546,10 +547,10 @@ namespace pcl /** \brief Set the voxel grid leaf size. * \param[in] leaf_size the voxel grid leaf size */ - inline void - setLeafSize (const Eigen::Vector4f &leaf_size) - { - leaf_size_ = leaf_size; + inline void + setLeafSize (const Eigen::Vector4f &leaf_size) + { + leaf_size_ = leaf_size; // Avoid division errors if (leaf_size_[3] == 0) leaf_size_[3] = 1; @@ -574,65 +575,65 @@ namespace pcl } /** \brief Get the voxel grid leaf size. */ - inline Eigen::Vector3f - getLeafSize () { return (leaf_size_.head<3> ()); } + inline Eigen::Vector3f + getLeafSize () const { return (leaf_size_.head<3> ()); } /** \brief Set to true if all fields need to be downsampled, or false if just XYZ. * \param[in] downsample the new value (true/false) */ - inline void + inline void setDownsampleAllData (bool downsample) { downsample_all_data_ = downsample; } /** \brief Get the state of the internal downsampling parameter (true if - * all fields need to be downsampled, false if just XYZ). + * all fields need to be downsampled, false if just XYZ). */ - inline bool - getDownsampleAllData () { return (downsample_all_data_); } + inline bool + getDownsampleAllData () const { return (downsample_all_data_); } /** \brief Set the minimum number of points required for a voxel to be used. * \param[in] min_points_per_voxel the minimum number of points for required for a voxel to be used */ - inline void + inline void setMinimumPointsNumberPerVoxel (unsigned int min_points_per_voxel) { min_points_per_voxel_ = min_points_per_voxel; } /** \brief Return the minimum number of points required for a voxel to be used. */ inline unsigned int - getMinimumPointsNumberPerVoxel () { return min_points_per_voxel_; } + getMinimumPointsNumberPerVoxel () const { return min_points_per_voxel_; } /** \brief Set to true if leaf layout information needs to be saved for later access. * \param[in] save_leaf_layout the new value (true/false) */ - inline void + inline void setSaveLeafLayout (bool save_leaf_layout) { save_leaf_layout_ = save_leaf_layout; } /** \brief Returns true if leaf layout information will to be saved for later access. */ - inline bool - getSaveLeafLayout () { return (save_leaf_layout_); } + inline bool + getSaveLeafLayout () const { return (save_leaf_layout_); } /** \brief Get the minimum coordinates of the bounding box (after - * filtering is performed). + * filtering is performed). */ - inline Eigen::Vector3i - getMinBoxCoordinates () { return (min_b_.head<3> ()); } + inline Eigen::Vector3i + getMinBoxCoordinates () const { return (min_b_.head<3> ()); } /** \brief Get the minimum coordinates of the bounding box (after - * filtering is performed). + * filtering is performed). */ - inline Eigen::Vector3i - getMaxBoxCoordinates () { return (max_b_.head<3> ()); } + inline Eigen::Vector3i + getMaxBoxCoordinates () const { return (max_b_.head<3> ()); } /** \brief Get the number of divisions along all 3 axes (after filtering - * is performed). + * is performed). */ - inline Eigen::Vector3i - getNrDivisions () { return (div_b_.head<3> ()); } + inline Eigen::Vector3i + getNrDivisions () const { return (div_b_.head<3> ()); } /** \brief Get the multipliers to be applied to the grid coordinates in - * order to find the centroid index (after filtering is performed). + * order to find the centroid index (after filtering is performed). */ - inline Eigen::Vector3i - getDivisionMultiplier () { return (divb_mul_.head<3> ()); } + inline Eigen::Vector3i + getDivisionMultiplier () const { return (divb_mul_.head<3> ()); } /** \brief Returns the index in the resulting downsampled cloud of the specified point. * \note for efficiency, user must make sure that the saving of the leaf layout is enabled and filtering performed, @@ -641,13 +642,13 @@ namespace pcl * \param[in] y the Y point coordinate to get the index at * \param[in] z the Z point coordinate to get the index at */ - inline int - getCentroidIndex (float x, float y, float z) + inline int + getCentroidIndex (float x, float y, float z) const { - return (leaf_layout_.at ((Eigen::Vector4i (static_cast (floor (x * inverse_leaf_size_[0])), - static_cast (floor (y * inverse_leaf_size_[1])), - static_cast (floor (z * inverse_leaf_size_[2])), - 0) + return (leaf_layout_.at ((Eigen::Vector4i (static_cast (floor (x * inverse_leaf_size_[0])), + static_cast (floor (y * inverse_leaf_size_[1])), + static_cast (floor (z * inverse_leaf_size_[2])), + 0) - min_b_).dot (divb_mul_))); } @@ -659,11 +660,11 @@ namespace pcl * \param[out] relative_coordinates matrix with the columns being the coordinates of the requested cells, relative to the reference point's cell * \note for efficiency, user must make sure that the saving of the leaf layout is enabled and filtering performed */ - inline std::vector - getNeighborCentroidIndices (float x, float y, float z, const Eigen::MatrixXi &relative_coordinates) + inline std::vector + getNeighborCentroidIndices (float x, float y, float z, const Eigen::MatrixXi &relative_coordinates) const { - Eigen::Vector4i ijk (static_cast (floor (x * inverse_leaf_size_[0])), - static_cast (floor (y * inverse_leaf_size_[1])), + Eigen::Vector4i ijk (static_cast (floor (x * inverse_leaf_size_[0])), + static_cast (floor (y * inverse_leaf_size_[1])), static_cast (floor (z * inverse_leaf_size_[2])), 0); Eigen::Array4i diff2min = min_b_ - ijk; Eigen::Array4i diff2max = max_b_ - ijk; @@ -679,7 +680,7 @@ namespace pcl } return (neighbors); } - + /** \brief Returns the indices in the resulting downsampled cloud of the points at the specified grid coordinates, * relative to the grid coordinates of the specified point (or -1 if the cell was empty/out of bounds). * \param[in] x the X coordinate of the reference point (corresponding cell is allowed to be empty/out of bounds) @@ -688,8 +689,8 @@ namespace pcl * \param[out] relative_coordinates vector with the elements being the coordinates of the requested cells, relative to the reference point's cell * \note for efficiency, user must make sure that the saving of the leaf layout is enabled and filtering performed */ - inline std::vector - getNeighborCentroidIndices (float x, float y, float z, const std::vector > &relative_coordinates) + inline std::vector + getNeighborCentroidIndices (float x, float y, float z, const std::vector > &relative_coordinates) const { Eigen::Vector4i ijk (static_cast (floorf (x * inverse_leaf_size_[0])), static_cast (floorf (y * inverse_leaf_size_[1])), static_cast (floorf (z * inverse_leaf_size_[2])), 0); std::vector neighbors; @@ -702,27 +703,27 @@ namespace pcl /** \brief Returns the layout of the leafs for fast access to cells relative to current position. * \note position at (i-min_x) + (j-min_y)*div_x + (k-min_z)*div_x*div_y holds the index of the element at coordinates (i,j,k) in the grid (-1 if empty) */ - inline std::vector - getLeafLayout () { return (leaf_layout_); } + inline std::vector + getLeafLayout () const { return (leaf_layout_); } /** \brief Returns the corresponding (i,j,k) coordinates in the grid of point (x,y,z). * \param[in] x the X point coordinate to get the (i, j, k) index at * \param[in] y the Y point coordinate to get the (i, j, k) index at * \param[in] z the Z point coordinate to get the (i, j, k) index at */ - inline Eigen::Vector3i - getGridCoordinates (float x, float y, float z) - { - return (Eigen::Vector3i (static_cast (floor (x * inverse_leaf_size_[0])), - static_cast (floor (y * inverse_leaf_size_[1])), - static_cast (floor (z * inverse_leaf_size_[2])))); + inline Eigen::Vector3i + getGridCoordinates (float x, float y, float z) const + { + return (Eigen::Vector3i (static_cast (floor (x * inverse_leaf_size_[0])), + static_cast (floor (y * inverse_leaf_size_[1])), + static_cast (floor (z * inverse_leaf_size_[2])))); } /** \brief Returns the index in the downsampled cloud corresponding to a given set of coordinates. * \param[in] ijk the coordinates (i,j,k) in the grid (-1 if empty) */ - inline int - getCentroidIndexAt (const Eigen::Vector3i &ijk) + inline int + getCentroidIndexAt (const Eigen::Vector3i &ijk) const { int idx = ((Eigen::Vector4i() << ijk, 0).finished() - min_b_).dot (divb_mul_); if (idx < 0 || idx >= static_cast (leaf_layout_.size ())) // this checks also if leaf_layout_.size () == 0 i.e. everything was computed as needed @@ -746,7 +747,7 @@ namespace pcl /** \brief Get the name of the field used for filtering. */ inline std::string const - getFilterFieldName () + getFilterFieldName () const { return (filter_field_name_); } @@ -762,12 +763,12 @@ namespace pcl filter_limit_max_ = limit_max; } - /** \brief Get the field filter limits (min/max) set by the user. The default values are -FLT_MAX, FLT_MAX. + /** \brief Get the field filter limits (min/max) set by the user. The default values are -FLT_MAX, FLT_MAX. * \param[out] limit_min the minimum allowed field value * \param[out] limit_max the maximum allowed field value */ inline void - getFilterLimits (double &limit_min, double &limit_max) + getFilterLimits (double &limit_min, double &limit_max) const { limit_min = filter_limit_min_; limit_max = filter_limit_max_; @@ -783,20 +784,20 @@ namespace pcl filter_limit_negative_ = limit_negative; } - /** \brief Get whether the data outside the interval (min/max) is to be returned (true) or inside (false). + /** \brief Get whether the data outside the interval (min/max) is to be returned (true) or inside (false). * \param[out] limit_negative true if data \b outside the interval [min; max] is to be returned, false otherwise */ inline void - getFilterLimitsNegative (bool &limit_negative) + getFilterLimitsNegative (bool &limit_negative) const { limit_negative = filter_limit_negative_; } - /** \brief Get whether the data outside the interval (min/max) is to be returned (true) or inside (false). + /** \brief Get whether the data outside the interval (min/max) is to be returned (true) or inside (false). * \return true if data \b outside the interval [min; max] is to be returned, false otherwise */ inline bool - getFilterLimitsNegative () + getFilterLimitsNegative () const { return (filter_limit_negative_); } @@ -805,24 +806,24 @@ namespace pcl /** \brief The size of a leaf. */ Eigen::Vector4f leaf_size_; - /** \brief Internal leaf sizes stored as 1/leaf_size_ for efficiency reasons. */ + /** \brief Internal leaf sizes stored as 1/leaf_size_ for efficiency reasons. */ Eigen::Array4f inverse_leaf_size_; /** \brief Set to true if all fields need to be downsampled, or false if just XYZ. */ bool downsample_all_data_; /** \brief Set to true if leaf layout information needs to be saved in \a - * leaf_layout. + * leaf_layout. */ bool save_leaf_layout_; /** \brief The leaf layout information for fast access to cells relative - * to current position + * to current position */ std::vector leaf_layout_; /** \brief The minimum and maximum bin coordinates, the number of - * divisions, and the division multiplier. + * divisions, and the division multiplier. */ Eigen::Vector4i min_b_, max_b_, div_b_, divb_mul_; @@ -844,7 +845,7 @@ namespace pcl /** \brief Downsample a Point Cloud using a voxelized grid approach * \param[out] output the resultant point cloud */ - void + void applyFilter (PCLPointCloud2 &output); }; } diff --git a/filters/include/pcl/filters/voxel_grid_covariance.h b/filters/include/pcl/filters/voxel_grid_covariance.h index d4e6a094..e181986d 100644 --- a/filters/include/pcl/filters/voxel_grid_covariance.h +++ b/filters/include/pcl/filters/voxel_grid_covariance.h @@ -367,7 +367,7 @@ namespace pcl } - /** \brief Get the voxels surrounding point p, not including the voxel contating point p. + /** \brief Get the voxels surrounding point p, not including the voxel containing point p. * \note Only voxels containing a sufficient number of points are used (slower than radius search in practice). * \param[in] reference_point the point to get the leaf structure at * \param[out] neighbors @@ -520,7 +520,7 @@ namespace pcl /** \brief Flag to determine if voxel structure is searchable. */ bool searchable_; - /** \brief Minimum points contained with in a voxel to allow it to be useable. */ + /** \brief Minimum points contained with in a voxel to allow it to be usable. */ int min_points_per_voxel_; /** \brief Minimum allowable ratio between eigenvalues to prevent singular covariance matrices. */ diff --git a/filters/include/pcl/filters/voxel_grid_occlusion_estimation.h b/filters/include/pcl/filters/voxel_grid_occlusion_estimation.h index cd0de5ba..857dd11e 100644 --- a/filters/include/pcl/filters/voxel_grid_occlusion_estimation.h +++ b/filters/include/pcl/filters/voxel_grid_occlusion_estimation.h @@ -113,7 +113,7 @@ namespace pcl const Eigen::Vector3i& in_target_voxel); /** \brief Computes the voxel coordinates (i, j, k) of all occluded - * voxels in the voxel gird. + * voxels in the voxel grid. * \param[out] occluded_voxels the coordinates (i, j, k) of all occluded voxels * \return 0 upon success and -1 if an error occurs */ diff --git a/filters/src/extract_indices.cpp b/filters/src/extract_indices.cpp index ab88180e..94661961 100644 --- a/filters/src/extract_indices.cpp +++ b/filters/src/extract_indices.cpp @@ -186,7 +186,7 @@ pcl::ExtractIndices::applyFilter (std::vector &indices #include #ifdef PCL_ONLY_CORE_POINT_TYPES - PCL_INSTANTIATE(ExtractIndices, (pcl::PointXYZ)(pcl::PointXYZI)(pcl::PointXYZRGB)(pcl::PointXYZRGBA)(pcl::Normal)(pcl::PointXYZRGBNormal)) + PCL_INSTANTIATE(ExtractIndices, (pcl::PointXYZ)(pcl::PointXYZI)(pcl::PointXYZRGB)(pcl::PointXYZRGBA)(pcl::Normal)(pcl::PointNormal)(pcl::PointXYZRGBNormal)) #else PCL_INSTANTIATE(ExtractIndices, PCL_POINT_TYPES) #endif diff --git a/filters/src/passthrough.cpp b/filters/src/passthrough.cpp index 90e9f7f7..e7fe2461 100644 --- a/filters/src/passthrough.cpp +++ b/filters/src/passthrough.cpp @@ -86,7 +86,7 @@ pcl::PassThrough::applyFilter (PCLPointCloud2 &output) { // Copy the header (and thus the frame_id) + allocate enough space for points output.height = 1; // filtering breaks the organized structure - // Because we're doing explit checks for isfinite, is_dense = true + // Because we're doing explicit checks for isfinite, is_dense = true output.is_dense = true; } output.row_step = input_->row_step; diff --git a/filters/src/statistical_outlier_removal.cpp b/filters/src/statistical_outlier_removal.cpp index adf59d84..2f8ab7d9 100644 --- a/filters/src/statistical_outlier_removal.cpp +++ b/filters/src/statistical_outlier_removal.cpp @@ -41,11 +41,12 @@ #include #include +using namespace std; + /////////////////////////////////////////////////////////////////////////////////////////// void pcl::StatisticalOutlierRemoval::applyFilter (PCLPointCloud2 &output) { - output.is_dense = true; // If fields x/y/z are not present, we cannot filter if (x_idx_ == -1 || y_idx_ == -1 || z_idx_ == -1) { @@ -62,6 +63,133 @@ pcl::StatisticalOutlierRemoval::applyFilter (PCLPointCloud2 output.data.clear (); return; } + + double mean; + double variance; + double stddev; + vector distances; + generateStatistics (mean, variance, stddev, distances); + double const distance_threshold = mean + std_mul_ * stddev; // a distance that is bigger than this signals an outlier + + // Copy the common fields + output.is_dense = input_->is_dense; + output.is_bigendian = input_->is_bigendian; + output.point_step = input_->point_step; + if (keep_organized_) + { + output.width = input_->width; + output.height = input_->height; + output.data.resize (input_->data.size ()); + } + else + { + output.height = 1; + output.data.resize (indices_->size () * input_->point_step); // reserve enough space + } + + removed_indices_->resize (input_->data.size ()); + + // Build a new cloud by neglecting outliers + int nr_p = 0; + int nr_removed_p = 0; + bool remove_point = false; + for (int cp = 0; cp < static_cast (indices_->size ()); ++cp) + { + if (negative_) + remove_point = (distances[cp] <= distance_threshold); + else + remove_point = (distances[cp] > distance_threshold); + + if (remove_point) + { + if (extract_removed_indices_) + (*removed_indices_)[nr_removed_p++] = cp; + + if (keep_organized_) + { + /* Set the current point to NaN. */ + *(reinterpret_cast(&output.data[nr_p * output.point_step])+0) = std::numeric_limits::quiet_NaN(); + *(reinterpret_cast(&output.data[nr_p * output.point_step])+1) = std::numeric_limits::quiet_NaN(); + *(reinterpret_cast(&output.data[nr_p * output.point_step])+2) = std::numeric_limits::quiet_NaN(); + nr_p++; + output.is_dense = false; + } + else + continue; + } + else + { + memcpy (&output.data[nr_p * output.point_step], &input_->data[(*indices_)[cp] * output.point_step], + output.point_step); + nr_p++; + } + } + + if (!keep_organized_) + { + output.width = nr_p; + output.data.resize (output.width * output.point_step); + } + output.row_step = output.point_step * output.width; + + removed_indices_->resize (nr_removed_p); +} + +/////////////////////////////////////////////////////////////////////////////////////////// +void +pcl::StatisticalOutlierRemoval::applyFilter (vector& indices) +{ + // If fields x/y/z are not present, we cannot filter + if (x_idx_ == -1 || y_idx_ == -1 || z_idx_ == -1) + { + PCL_ERROR ("[pcl::%s::applyFilter] Input dataset doesn't have x-y-z coordinates!\n", getClassName ().c_str ()); + indices.clear(); + return; + } + + if (std_mul_ == 0.0) + { + PCL_ERROR ("[pcl::%s::applyFilter] Standard deviation multipler not set!\n", getClassName ().c_str ()); + indices.clear(); + return; + } + + double mean; + double variance; + double stddev; + vector distances; + generateStatistics(mean, variance, stddev, distances); + double const distance_threshold = mean + std_mul_ * stddev; // a distance that is bigger than this signals an outlier + + // Second pass: Classify the points on the computed distance threshold + size_t nr_p = 0, nr_removed_p = 0; + for (size_t cp = 0; cp < indices_->size (); ++cp) + { + // Points having a too high average distance are outliers and are passed to removed indices + // Unless negative was set, then it's the opposite condition + if ((!negative_ && distances[cp] > distance_threshold) || (negative_ && distances[cp] <= distance_threshold)) + { + if (extract_removed_indices_) + (*removed_indices_)[nr_removed_p++] = (*indices_)[cp]; + continue; + } + + // Otherwise it was a normal point for output (inlier) + indices[nr_p++] = (*indices_)[cp]; + } + + // Resize the output arrays + indices.resize (nr_p); + removed_indices_->resize (nr_p); +} + +/////////////////////////////////////////////////////////////////////////////////////////// +void +pcl::StatisticalOutlierRemoval::generateStatistics (double& mean, + double& variance, + double& stddev, + std::vector& distances) +{ // Send the input dataset to the spatial locator pcl::PointCloud::Ptr cloud (new pcl::PointCloud); pcl::fromPCLPointCloud2 (*input_, *cloud); @@ -81,7 +209,7 @@ pcl::StatisticalOutlierRemoval::applyFilter (PCLPointCloud2 std::vector nn_indices (mean_k_); std::vector nn_dists (mean_k_); - std::vector distances (indices_->size ()); + distances.resize (indices_->size ()); int valid_distances = 0; // Go over all the points and calculate the mean or smallest distance for (size_t cp = 0; cp < indices_->size (); ++cp) @@ -116,62 +244,13 @@ pcl::StatisticalOutlierRemoval::applyFilter (PCLPointCloud2 sum += distances[i]; sq_sum += distances[i] * distances[i]; } - double mean = sum / static_cast(valid_distances); - double variance = (sq_sum - sum * sum / static_cast(valid_distances)) / (static_cast(valid_distances) - 1); - double stddev = sqrt (variance); - //getMeanStd (distances, mean, stddev); - double distance_threshold = mean + std_mul_ * stddev; // a distance that is bigger than this signals an outlier - - // Copy the common fields - output.is_bigendian = input_->is_bigendian; - output.point_step = input_->point_step; - output.height = 1; - - output.data.resize (indices_->size () * input_->point_step); // reserve enough space - removed_indices_->resize (input_->data.size ()); - - // Build a new cloud by neglecting outliers - int nr_p = 0; - int nr_removed_p = 0; - for (int cp = 0; cp < static_cast (indices_->size ()); ++cp) - { - if (negative_) - { - if (distances[cp] <= distance_threshold) - { - if (extract_removed_indices_) - { - (*removed_indices_)[nr_removed_p] = cp; - nr_removed_p++; - } - continue; - } - } - else - { - if (distances[cp] > distance_threshold) - { - if (extract_removed_indices_) - { - (*removed_indices_)[nr_removed_p] = cp; - nr_removed_p++; - } - continue; - } - } - - memcpy (&output.data[nr_p * output.point_step], &input_->data[(*indices_)[cp] * output.point_step], - output.point_step); - nr_p++; - } - output.width = nr_p; - output.data.resize (output.width * output.point_step); - output.row_step = output.point_step * output.width; - - removed_indices_->resize (nr_removed_p); + mean = sum / static_cast(valid_distances); + variance = (sq_sum - sum * sum / static_cast(valid_distances)) / (static_cast(valid_distances) - 1); + stddev = sqrt (variance); } + #ifndef PCL_NO_PRECOMPILE #include #include diff --git a/geometry/include/pcl/geometry/mesh_traits.h b/geometry/include/pcl/geometry/mesh_traits.h index 0c23243a..01a34a92 100644 --- a/geometry/include/pcl/geometry/mesh_traits.h +++ b/geometry/include/pcl/geometry/mesh_traits.h @@ -77,7 +77,7 @@ namespace pcl typedef EdgeDataT EdgeData; typedef FaceDataT FaceData; - /** \brief Specifies wether the mesh is manifold or not (only non-manifold vertices can be represented). */ + /** \brief Specifies whether the mesh is manifold or not (only non-manifold vertices can be represented). */ typedef boost::false_type IsManifold; }; } // End namespace geometry diff --git a/gpu/containers/include/pcl/gpu/containers/device_array.h b/gpu/containers/include/pcl/gpu/containers/device_array.h index 03da2aa6..c1ed2ebb 100644 --- a/gpu/containers/include/pcl/gpu/containers/device_array.h +++ b/gpu/containers/include/pcl/gpu/containers/device_array.h @@ -73,18 +73,18 @@ namespace pcl /** \brief Initializes with user allocated buffer. Reference counting is disabled in this case. * \param ptr pointer to buffer - * \param size elemens number + * \param size elements number * */ DeviceArray(T *ptr, size_t size); /** \brief Copy constructor. Just increments reference counter. */ DeviceArray(const DeviceArray& other); - /** \brief Assigment operator. Just increments reference counter. */ + /** \brief Assignment operator. Just increments reference counter. */ DeviceArray& operator = (const DeviceArray& other); /** \brief Allocates internal buffer in GPU memory. If internal buffer was created before the function recreates it with new size. If new and old sizes are equal it does nothing. - * \param size elemens number + * \param size elements number * */ void create(size_t size); @@ -98,7 +98,7 @@ namespace pcl /** \brief Uploads data to internal buffer in GPU memory. It calls create() inside to ensure that intenal buffer size is enough. * \param host_ptr pointer to buffer to upload - * \param size elemens number + * \param size elements number * */ void upload(const T *host_ptr, size_t size); @@ -180,7 +180,7 @@ namespace pcl /** \brief Copy constructor. Just increments reference counter. */ DeviceArray2D(const DeviceArray2D& other); - /** \brief Assigment operator. Just increments reference counter. */ + /** \brief Assignment operator. Just increments reference counter. */ DeviceArray2D& operator = (const DeviceArray2D& other); /** \brief Allocates internal buffer in GPU memory. If internal buffer was created before the function recreates it with new size. If new and old sizes are equal it does nothing. @@ -205,7 +205,7 @@ namespace pcl * */ void upload(const void *host_ptr, size_t host_step, int rows, int cols); - /** \brief Downloads data from internal buffer to CPU memory. User is resposible for correct host buffer size. + /** \brief Downloads data from internal buffer to CPU memory. User is responsible for correct host buffer size. * \param host_ptr pointer to host buffer to download * \param host_step stride between two consecutive rows in bytes for host buffer * */ diff --git a/gpu/containers/include/pcl/gpu/containers/device_memory.h b/gpu/containers/include/pcl/gpu/containers/device_memory.h index c8217f83..032813af 100644 --- a/gpu/containers/include/pcl/gpu/containers/device_memory.h +++ b/gpu/containers/include/pcl/gpu/containers/device_memory.h @@ -75,7 +75,7 @@ namespace pcl /** \brief Copy constructor. Just increments reference counter. */ DeviceMemory(const DeviceMemory& other_arg); - /** \brief Assigment operator. Just increments reference counter. */ + /** \brief Assignment operator. Just increments reference counter. */ DeviceMemory& operator=(const DeviceMemory& other_arg); /** \brief Allocates internal buffer in GPU memory. If internal buffer was created before the function recreates it with new size. If new and old sizes are equal it does nothing. @@ -167,7 +167,7 @@ namespace pcl /** \brief Copy constructor. Just increments reference counter. */ DeviceMemory2D(const DeviceMemory2D& other_arg); - /** \brief Assigment operator. Just increments reference counter. */ + /** \brief Assignment operator. Just increments reference counter. */ DeviceMemory2D& operator=(const DeviceMemory2D& other_arg); /** \brief Allocates internal buffer in GPU memory. If internal buffer was created before the function recreates it with new size. If new and old sizes are equal it does nothing. @@ -192,7 +192,7 @@ namespace pcl * */ void upload(const void *host_ptr_arg, size_t host_step_arg, int rows_arg, int colsBytes_arg); - /** \brief Downloads data from internal buffer to CPU memory. User is resposible for correct host buffer size. + /** \brief Downloads data from internal buffer to CPU memory. User is responsible for correct host buffer size. * \param host_ptr_arg pointer to host buffer to download * \param host_step_arg stride between two consecutive rows in bytes for host buffer * */ diff --git a/gpu/containers/include/pcl/gpu/containers/initialization.h b/gpu/containers/include/pcl/gpu/containers/initialization.h index b607b24c..6a6ef72b 100644 --- a/gpu/containers/include/pcl/gpu/containers/initialization.h +++ b/gpu/containers/include/pcl/gpu/containers/initialization.h @@ -50,20 +50,20 @@ namespace pcl /** \brief Sets active device to work with. */ PCL_EXPORTS void setDevice(int device); - /** \brief Return devuce name for gived device. */ + /** \brief Return device name for given device. */ PCL_EXPORTS std::string getDeviceName(int device); - /** \brief Prints infromatoin about given cuda deivce or about all deivces - * \param device: if < 0 prints info for all devices, otherwise the function interpets is as device id. + /** \brief Prints information about given cuda device or about all devices + * \param device: if < 0 prints info for all devices, otherwise the function interprets it as device id. */ void PCL_EXPORTS printCudaDeviceInfo(int device = -1); - /** \brief Prints infromatoin about given cuda deivce or about all deivces - * \param device: if < 0 prints info for all devices, otherwise the function interpets is as device id. + /** \brief Prints information about given cuda device or about all devices + * \param device: if < 0 prints info for all devices, otherwise the function interprets it as device id. */ void PCL_EXPORTS printShortCudaDeviceInfo(int device = -1); - /** \brief Returns true if pre-Fermi generaton GPU. + /** \brief Returns true if pre-Fermi generator GPU. * \param device: device id to check, if < 0 checks current device. */ bool PCL_EXPORTS checkIfPreFermiGPU(int device = -1); diff --git a/gpu/containers/src/initialization.cpp b/gpu/containers/src/initialization.cpp index 17152b7d..653a8326 100644 --- a/gpu/containers/src/initialization.cpp +++ b/gpu/containers/src/initialization.cpp @@ -50,7 +50,7 @@ void throw_nogpu() { throw "PCL 2.0 exception"; } int pcl::gpu::getCudaEnabledDeviceCount() { return 0; } void pcl::gpu::setDevice(int /*device*/) { throw_nogpu(); } std::string pcl::gpu::getDeviceName(int /*device*/) { throw_nogpu(); } -void pcl::gpu::printCudaDeviceInfo(int /*deivce*/){ throw_nogpu(); } +void pcl::gpu::printCudaDeviceInfo(int /*device*/){ throw_nogpu(); } void pcl::gpu::printShortCudaDeviceInfo(int /*device*/) { throw_nogpu(); } #else @@ -111,7 +111,7 @@ namespace { // Defines for GPU Architecture types (using the SM version to determine the # of cores per SM typedef struct { - int SM; // 0xMm (hexidecimal notation), M = SM Major version, and m = SM minor version + int SM; // 0xMm (hexadecimal notation), M = SM Major version, and m = SM minor version int Cores; } SMtoCores; diff --git a/gpu/features/CMakeLists.txt b/gpu/features/CMakeLists.txt index c1d8653b..aff62bde 100644 --- a/gpu/features/CMakeLists.txt +++ b/gpu/features/CMakeLists.txt @@ -31,7 +31,8 @@ if (build) PCL_MAKE_PKGCONFIG("${LIB_NAME}" "${SUBSYS_NAME}" "${SUBSYS_DESC}" "${SUBSYS_DEPS}" "${EXT_DEPS}" "" "" "") # Install include files - PCL_ADD_INCLUDES("${SUBSYS_NAME}" "${SUBSYS_PATH}" ${incs} ${dev_incs}) + PCL_ADD_INCLUDES("${SUBSYS_NAME}" "${SUBSYS_PATH}" ${incs}) + PCL_ADD_INCLUDES("${SUBSYS_NAME}" "${SUBSYS_PATH}/device" ${dev_incs}) add_subdirectory(test) endif() diff --git a/gpu/features/include/pcl/gpu/features/device/eigen.hpp b/gpu/features/include/pcl/gpu/features/device/eigen.hpp index 16b3b182..1c1525dc 100644 --- a/gpu/features/include/pcl/gpu/features/device/eigen.hpp +++ b/gpu/features/include/pcl/gpu/features/device/eigen.hpp @@ -154,7 +154,7 @@ namespace pcl if (roots.x >= roots.y) swap (roots.x, roots.y); } - if (roots.x <= 0) // eigenval for symetric positive semi-definite matrix can not be negative! Set it to 0 + if (roots.x <= 0) // eigenval for symmetric positive semi-definite matrix can not be negative! Set it to 0 computeRoots2 (c2, c1, roots); } } diff --git a/gpu/features/src/spinimages.cu b/gpu/features/src/spinimages.cu index a7578971..64b05e67 100644 --- a/gpu/features/src/spinimages.cu +++ b/gpu/features/src/spinimages.cu @@ -240,12 +240,12 @@ namespace pcl if (angular) { //transform sum to average dividing angle/spinimage element-wize. - const float *amgles_beg = simage_angles + FSize; - const float *amgles_end = amgles_beg + FSize; + const float *angles_beg = simage_angles + FSize; + const float *angles_end = angles_beg + FSize; const float *images_beg = simage_angles; - Block::transfrom(amgles_beg, amgles_end, images_beg, output.ptr(i_input), Div12eps()); - ////Block::copy(amgles_beg, amgles_end, output.ptr(i_input)); + Block::transform(angles_beg, angles_end, images_beg, output.ptr(i_input), Div12eps()); + ////Block::copy(angles_beg, angles_end, output.ptr(i_input)); //Block::copy(images_beg, images_beg + FSize, output.ptr(i_input)); } else @@ -259,7 +259,7 @@ namespace pcl __syncthreads(); float sum = simage_angles[FSize]; - Block::transfrom(simage_angles, simage_angles + FSize, output.ptr(i_input), DivValIfNonZero(sum)); + Block::transform(simage_angles, simage_angles + FSize, output.ptr(i_input), DivValIfNonZero(sum)); } } diff --git a/gpu/features/src/vfh.cu b/gpu/features/src/vfh.cu index 008c62d0..0278a3b4 100644 --- a/gpu/features/src/vfh.cu +++ b/gpu/features/src/vfh.cu @@ -238,10 +238,10 @@ void pcl::device::VFHEstimationImpl::compute(DeviceArray& featu cudaSafeCall( cudaGetDeviceProperties(&prop, device) ); int total = static_cast (indices.empty() ? points.size() : indices.size()); - int total_lenght_in_blocks = (total + VfhDevice::CTA_SIZE - 1) / VfhDevice::CTA_SIZE; + int total_length_in_blocks = (total + VfhDevice::CTA_SIZE - 1) / VfhDevice::CTA_SIZE; int block = VfhDevice::CTA_SIZE; - int grid = min(total_lenght_in_blocks, prop.multiProcessorCount * prop.maxThreadsPerMultiProcessor / VfhDevice::CTA_SIZE); + int grid = min(total_length_in_blocks, prop.multiProcessorCount * prop.maxThreadsPerMultiProcessor / VfhDevice::CTA_SIZE); DeviceArray2D global_buffer(grid, VfhDevice::FSize); diff --git a/gpu/features/test/data_source.hpp b/gpu/features/test/data_source.hpp index 2bc1839d..87457f70 100644 --- a/gpu/features/test/data_source.hpp +++ b/gpu/features/test/data_source.hpp @@ -35,8 +35,8 @@ */ -#ifndef PCL_GPU_FEATURES_TEST_DATA_SORUCE_HPP_ -#define PCL_GPU_FEATURES_TEST_DATA_SORUCE_HPP_ +#ifndef PCL_GPU_FEATURES_TEST_DATA_SOURCE_HPP_ +#define PCL_GPU_FEATURES_TEST_DATA_SOURCE_HPP_ #include @@ -210,4 +210,4 @@ namespace pcl } } -#endif /* PCL_GPU_FEATURES_TEST_DATA_SORUCE_HPP_ */ +#endif /* PCL_GPU_FEATURES_TEST_DATA_SOURCE_HPP_ */ diff --git a/gpu/kinfu/include/pcl/gpu/kinfu/color_volume.h b/gpu/kinfu/include/pcl/gpu/kinfu/color_volume.h index 512807f2..3e7d60f4 100644 --- a/gpu/kinfu/include/pcl/gpu/kinfu/color_volume.h +++ b/gpu/kinfu/include/pcl/gpu/kinfu/color_volume.h @@ -65,7 +65,7 @@ namespace pcl */ ColorVolume(const TsdfVolume& tsdf, int max_weight = -1); - /** \brief Desctructor */ + /** \brief Destructor */ ~ColorVolume(); /** \brief Resets color volume to uninitialized state */ diff --git a/gpu/kinfu/include/pcl/gpu/kinfu/kinfu.h b/gpu/kinfu/include/pcl/gpu/kinfu/kinfu.h index ec957f90..c7f8ddef 100644 --- a/gpu/kinfu/include/pcl/gpu/kinfu/kinfu.h +++ b/gpu/kinfu/include/pcl/gpu/kinfu/kinfu.h @@ -101,7 +101,7 @@ namespace pcl getDepthIntrinsics (float& fx, float& fy, float& cx, float& cy); - /** \brief Sets initial camera pose relative to volume coordiante space + /** \brief Sets initial camera pose relative to volume coordinate space * \param[in] pose Initial camera pose */ void @@ -281,7 +281,7 @@ namespace pcl /** \brief Camera movement threshold. TSDF is integrated iff a camera movement metric exceedes some value. */ float integration_metric_threshold_; - /** \brief ICP step is completelly disabled. Inly integratio now */ + /** \brief ICP step is completely disabled. Only integration now. */ bool disable_icp_; /** \brief Allocates all GPU internal buffers. diff --git a/gpu/kinfu/include/pcl/gpu/kinfu/marching_cubes.h b/gpu/kinfu/include/pcl/gpu/kinfu/marching_cubes.h index fa84b15e..3e3a8b56 100644 --- a/gpu/kinfu/include/pcl/gpu/kinfu/marching_cubes.h +++ b/gpu/kinfu/include/pcl/gpu/kinfu/marching_cubes.h @@ -76,8 +76,8 @@ namespace pcl /** \brief Runs marching cubes triangulation. * \param[in] tsdf - * \param[in] triangles_buffer Buffer for triangles. Its size determines max extracted triangles. If empty, it will be allocated with default size will be used. - * \return Array with triangles. Each 3 consequent poits belond to a single triangle. The returned array points to 'triangles_buffer' data. + * \param[in] triangles_buffer Buffer for triangles. Its size determines max extracted triangles. If empty, it will be allocated with default size to be used. + * \return Array with triangles. Each 3 consequent points belong to a single triangle. The returned array points to 'triangles_buffer' data. */ DeviceArray run(const TsdfVolume& tsdf, DeviceArray& triangles_buffer); @@ -86,13 +86,13 @@ namespace pcl /** \brief Edge table for marching cubes */ DeviceArray edgeTable_; - /** \brief Number of vertextes table for marching cubes */ + /** \brief Number of vertices table for marching cubes */ DeviceArray numVertsTable_; /** \brief Triangles table for marching cubes */ DeviceArray triTable_; - /** \brief Temporary buffer used by marching cubes (first row stores occuped voxes id, second number of vetexes, third poits offsets */ + /** \brief Temporary buffer used by marching cubes (first row stores occupied voxel id, second number of vertices, third points offsets */ DeviceArray2D occupied_voxels_buffer_; }; } diff --git a/gpu/kinfu/include/pcl/gpu/kinfu/raycaster.h b/gpu/kinfu/include/pcl/gpu/kinfu/raycaster.h index 8c417675..9c8e701d 100644 --- a/gpu/kinfu/include/pcl/gpu/kinfu/raycaster.h +++ b/gpu/kinfu/include/pcl/gpu/kinfu/raycaster.h @@ -82,7 +82,7 @@ namespace pcl void setIntrinsics(float fx = 525.f, float fy = 525.f, float cx = -1, float cy = -1); - /** \brief Runs raycasting algorithm from given camera pose. It writes results to internal fiels. + /** \brief Runs raycasting algorithm from given camera pose. It writes results to internal files. * \param[in] volume tsdf volume container * \param[in] camera_pose camera pose */ diff --git a/gpu/kinfu/include/pcl/gpu/kinfu/tsdf_volume.h b/gpu/kinfu/include/pcl/gpu/kinfu/tsdf_volume.h index f6d83420..f81f41fd 100644 --- a/gpu/kinfu/include/pcl/gpu/kinfu/tsdf_volume.h +++ b/gpu/kinfu/include/pcl/gpu/kinfu/tsdf_volume.h @@ -69,7 +69,7 @@ namespace pcl */ TsdfVolume(const Eigen::Vector3i& resolution); - /** \brief Sets Tsdf volume size for each dimention + /** \brief Sets Tsdf volume size for each dimension * \param[in] size size of tsdf volume in meters */ void @@ -81,7 +81,7 @@ namespace pcl void setTsdfTruncDist (float distance); - /** \brief Returns tsdf volume container that point to data in GPU memroy */ + /** \brief Returns tsdf volume container that point to data in GPU memory */ DeviceArray2D data() const; diff --git a/gpu/kinfu/src/cuda/extract.cu b/gpu/kinfu/src/cuda/extract.cu index 5cc2e63c..d678d169 100644 --- a/gpu/kinfu/src/cuda/extract.cu +++ b/gpu/kinfu/src/cuda/extract.cu @@ -90,7 +90,11 @@ namespace pcl __shared__ int cta_buffer[CTA_SIZE]; #endif -#if __CUDA_ARCH__ >= 120 +#if CUDA_VERSION >= 9000 + if (__all_sync (__activemask (), x >= VOLUME_X) + || __all_sync (__activemask (), y >= VOLUME_Y)) + return; +#elif __CUDA_ARCH__ >= 120 if (__all (x >= VOLUME_X) || __all (y >= VOLUME_Y)) return; #else @@ -187,8 +191,11 @@ namespace pcl } /* if (W != 0 && F != 1.f) */ } /* if (x < VOLUME_X && y < VOLUME_Y) */ - -#if __CUDA_ARCH__ >= 200 +#if CUDA_VERSION >= 9000 + int total_warp = __popc (__ballot_sync (__activemask (), local_count > 0)) + + __popc (__ballot_sync (__activemask (), local_count > 1)) + + __popc (__ballot_sync (__activemask (), local_count > 2)); +#elif __CUDA_ARCH__ >= 200 ///not we fulfilled points array at current iteration int total_warp = __popc (__ballot (local_count > 0)) + __popc (__ballot (local_count > 1)) + __popc (__ballot (local_count > 2)); #else diff --git a/gpu/kinfu/src/cuda/maps.cu b/gpu/kinfu/src/cuda/maps.cu index a336fee8..5813e746 100644 --- a/gpu/kinfu/src/cuda/maps.cu +++ b/gpu/kinfu/src/cuda/maps.cu @@ -163,7 +163,7 @@ namespace pcl if (x < cols && y < rows) { - //vetexes + //vertices float3 vsrc, vdst = make_float3 (qnan, qnan, qnan); vsrc.x = vmap_src.ptr (y)[x]; diff --git a/gpu/kinfu/src/cuda/marching_cubes.cu b/gpu/kinfu/src/cuda/marching_cubes.cu index 89990d16..dbd5a528 100644 --- a/gpu/kinfu/src/cuda/marching_cubes.cu +++ b/gpu/kinfu/src/cuda/marching_cubes.cu @@ -143,7 +143,11 @@ namespace pcl #endif -#if __CUDA_ARCH__ >= 120 +#if CUDA_VERSION >= 9000 + if (__all_sync (__activemask (), x >= VOLUME_X) + || __all_sync (__activemask (), y >= VOLUME_Y)) + return; +#elif __CUDA_ARCH__ >= 200 if (__all (x >= VOLUME_X) || __all (y >= VOLUME_Y)) return; #else @@ -169,7 +173,9 @@ namespace pcl // read number of vertices from texture numVerts = (cubeindex == 0 || cubeindex == 255) ? 0 : tex1Dfetch (numVertsTex, cubeindex); } -#if __CUDA_ARCH__ >= 200 +#if CUDA_VERSION >= 9000 + int total = __popc (__ballot_sync (__activemask (), numVerts > 0)); +#elif __CUDA_ARCH__ >= 200 int total = __popc (__ballot (numVerts > 0)); #else int total = __popc (Emulation::Ballot(numVerts > 0, cta_buffer)); @@ -184,7 +190,9 @@ namespace pcl } int old_global_voxels_count = warps_buffer[warp_id]; -#if __CUDA_ARCH__ >= 200 +#if CUDA_VERSION >= 9000 + int offs = Warp::binaryExclScan (__ballot_sync (__activemask (), numVerts > 0)); +#elif __CUDA_ARCH__ >= 200 int offs = Warp::binaryExclScan (__ballot (numVerts > 0)); #else int offs = Warp::binaryExclScan(Emulation::Ballot(numVerts > 0, cta_buffer)); diff --git a/gpu/kinfu/src/cuda/tsdf_volume.cu b/gpu/kinfu/src/cuda/tsdf_volume.cu index a20a559d..98de8fa2 100644 --- a/gpu/kinfu/src/cuda/tsdf_volume.cu +++ b/gpu/kinfu/src/cuda/tsdf_volume.cu @@ -130,7 +130,7 @@ namespace pcl { float3 v_g = getVoxelGCoo (x, y, z); //3 // p - //tranform to curr cam coo space + //transform to curr cam coo space float3 v = Rcurr_inv * (v_g - tcurr); //4 int2 coo; //project to current cam diff --git a/gpu/kinfu/src/cuda/utils.hpp b/gpu/kinfu/src/cuda/utils.hpp index b333a3d0..b444713e 100644 --- a/gpu/kinfu/src/cuda/utils.hpp +++ b/gpu/kinfu/src/cuda/utils.hpp @@ -39,6 +39,7 @@ #ifndef PCL_GPU_KINFU_CUDA_UTILS_HPP_ #define PCL_GPU_KINFU_CUDA_UTILS_HPP_ +#include namespace pcl { @@ -182,7 +183,7 @@ namespace pcl if (roots.x >= roots.y) swap (roots.x, roots.y); } - if (roots.x <= 0) // eigenval for symetric positive semi-definite matrix can not be negative! Set it to 0 + if (roots.x <= 0) // eigenval for symmetric positive semi-definite matrix can not be negative! Set it to 0 computeRoots2 (c2, c1, roots); } } @@ -602,9 +603,12 @@ namespace pcl static __forceinline__ __device__ int Ballot(int predicate, volatile int* cta_buffer) { -#if __CUDA_ARCH__ >= 200 +#if CUDA_VERSION >= 9000 + (void)cta_buffer; + return __ballot_sync (__activemask (), predicate); +#elif __CUDA_ARCH__ >= 200 (void)cta_buffer; - return __ballot(predicate); + return __ballot(predicate); #else int tid = Block::flattenedThreadId(); cta_buffer[tid] = predicate ? (1 << (tid & 31)) : 0; @@ -615,7 +619,10 @@ namespace pcl static __forceinline__ __device__ bool All(int predicate, volatile int* cta_buffer) { -#if __CUDA_ARCH__ >= 200 +#if CUDA_VERSION >= 9000 + (void)cta_buffer; + return __all_sync (__activemask (), predicate); +#elif __CUDA_ARCH__ >= 200 (void)cta_buffer; return __all(predicate); #else diff --git a/gpu/kinfu/src/internal.h b/gpu/kinfu/src/internal.h index b5d0a227..aee57c81 100644 --- a/gpu/kinfu/src/internal.h +++ b/gpu/kinfu/src/internal.h @@ -95,8 +95,8 @@ namespace pcl /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Maps - /** \brief Perfoms bilateral filtering of disparity map - * \param[in] src soruce map + /** \brief Performs bilateral filtering of disparity map + * \param[in] src source map * \param[out] dst output map */ void @@ -131,7 +131,7 @@ namespace pcl void computeNormalsEigen (const MapArr& vmap, MapArr& nmap); - /** \brief Performs affine tranform of vertex and normal maps + /** \brief Performs affine transform of vertex and normal maps * \param[in] vmap_src source vertex map * \param[in] nmap_src source vertex map * \param[in] Rmat Rotation mat @@ -152,7 +152,7 @@ namespace pcl /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ICP - /** \brief (now it's exra code) Computes corespondances map + /** \brief (now it's extra code) Computes corespondances map * \param[in] vmap_g_curr current vertex map in global coo space * \param[in] nmap_g_curr current normals map in global coo space * \param[in] Rprev_inv inverse camera rotation at previous pose @@ -168,7 +168,7 @@ namespace pcl findCoresp (const MapArr& vmap_g_curr, const MapArr& nmap_g_curr, const Mat33& Rprev_inv, const float3& tprev, const Intr& intr, const MapArr& vmap_g_prev, const MapArr& nmap_g_prev, float distThres, float angleThres, PtrStepSz coresp); - /** \brief (now it's exra code) Computation Ax=b for ICP iteration + /** \brief (now it's extra code) Computation Ax=b for ICP iteration * \param[in] v_dst destination vertex map (previous frame cloud) * \param[in] n_dst destination normal map (previous frame normals) * \param[in] v_src source normal map (current frame cloud) @@ -289,7 +289,7 @@ namespace pcl const PtrStep& volume, MapArr& vmap, MapArr& nmap); /** \brief Renders 3D image of the scene - * \param[in] vmap vetex map + * \param[in] vmap vertex map * \param[in] nmap normals map * \param[in] light poase of light source * \param[out] dst buffer where image is generated @@ -335,13 +335,13 @@ namespace pcl /** \brief Perform point cloud extraction from tsdf volume * \param[in] volume tsdf volume * \param[in] volume_size size of the volume - * \param[out] output buffer large enought to store point cloud + * \param[out] output buffer large enough to store point cloud * \return number of point stored to passed buffer */ PCL_EXPORTS size_t extractCloud (const PtrStep& volume, const float3& volume_size, PtrSz output); - /** \brief Performs normals computation for given poins using tsdf volume + /** \brief Performs normals computation for given points using tsdf volume * \param[in] volume tsdf volume * \param[in] volume_size volume size * \param[in] input points where normals are computed @@ -413,16 +413,16 @@ namespace pcl void unbindTextures(); - /** \brief Scans tsdf volume and retrieves occuped voxes + /** \brief Scans tsdf volume and retrieves occupied voxels * \param[in] volume tsdf volume - * \param[out] occupied_voxels buffer for occuped voxels. The function fulfills first row with voxel ids and second row with number of vertextes. + * \param[out] occupied_voxels buffer for occupied voxels. The function fulfills first row with voxel ids and second row with number of vertices. * \return number of voxels in the buffer */ int getOccupiedVoxels(const PtrStep& volume, DeviceArray2D& occupied_voxels); /** \brief Computes total number of vertexes for all voxels and offsets of vertexes in final triangle array - * \param[out] occupied_voxels buffer with occuped voxels. The function fulfills 3nd only with offsets + * \param[out] occupied_voxels buffer with occupied voxels. The function fulfills 3nd only with offsets * \return total number of vertexes */ int @@ -430,7 +430,7 @@ namespace pcl /** \brief Generates final triangle array * \param[in] volume tsdf volume - * \param[in] occupied_voxels occuped voxel ids (first row), number of vertexes(second row), offsets(third row). + * \param[in] occupied_voxels occupied voxel ids (first row), number of vertexes(second row), offsets(third row). * \param[in] volume_size volume size in meters * \param[out] output triangle array */ diff --git a/gpu/kinfu/src/kinfu.cpp b/gpu/kinfu/src/kinfu.cpp index a92cad44..4675659f 100644 --- a/gpu/kinfu/src/kinfu.cpp +++ b/gpu/kinfu/src/kinfu.cpp @@ -222,7 +222,7 @@ pcl::gpu::KinfuTracker::allocateBufffers (int rows, int cols) coresps_[i].create (pyr_rows, pyr_cols); } depthRawScaled_.create (rows, cols); - // see estimate tranform for the magic numbers + // see estimate transform for the magic numbers gbuf_.create (27, 20*60); sumbuf_.create (27); } @@ -297,7 +297,7 @@ pcl::gpu::KinfuTracker::operator() (const DepthMap& depth_raw, } else { - Rcurr = Rprev; // tranform to global coo for ith camera pose + Rcurr = Rprev; // transform to global coo for ith camera pose tcurr = tprev; } { @@ -365,7 +365,7 @@ pcl::gpu::KinfuTracker::operator() (const DepthMap& depth_raw, } } } - //save tranform + //save transform rmats_.push_back (Rcurr); tvecs_.push_back (tcurr); } diff --git a/gpu/kinfu/tools/kinfu_app_sim.cpp b/gpu/kinfu/tools/kinfu_app_sim.cpp index cf0f7f81..166fff74 100644 --- a/gpu/kinfu/tools/kinfu_app_sim.cpp +++ b/gpu/kinfu/tools/kinfu_app_sim.cpp @@ -396,7 +396,7 @@ display_tic_toc (vector &tic_toc,const string &fun_name) void capture (Eigen::Isometry3d pose_in,unsigned short* depth_buffer_mm,const uint8_t* color_buffer)//, string point_cloud_fname) { - // No reference image - but this is kept for compatability with range_test_v2: + // No reference image - but this is kept for compatibility with range_test_v2: float* reference = new float[range_likelihood_->getRowHeight() * range_likelihood_->getColWidth()]; //const float* depth_buffer = range_likelihood_->getDepthBuffer(); // Copy one image from our last as a reference. @@ -1078,7 +1078,7 @@ struct KinFuApp PtrStepSz rgb24_sim = PtrStepSz(height, width, color_buf_, width); tic_toc.push_back (getTime ()); - if (1==0){ // live capture - probably doesnt work anymore, left in here for comparison: + if (1==0){ // live capture - probably doesn't work anymore, left in here for comparison: bool has_frame = evaluation_ptr_ ? evaluation_ptr_->grab(i, depth) : capture_.grab (depth, rgb24); if (!has_frame) { @@ -1382,7 +1382,7 @@ print_cli_help () cout << " --registration, -r : enable registration mode" << endl; cout << " --integrate-colors, -ic : enable color integration mode ( allows to get cloud with colors )" << endl; cout << " -volume_suze : define integration volume size" << endl; - cout << " -dev , -oni : select depth source. Default will be selected if not specified" << endl; + cout << " -dev , -oni : select depth source. Default will be selected if not specified" << endl; cout << ""; cout << " For RGBD benchmark (Requires OpenCV):" << endl; cout << " -eval [-match_file ]" << endl; diff --git a/gpu/kinfu/tools/record_tsdfvolume.cpp b/gpu/kinfu/tools/record_tsdfvolume.cpp index 3d8d0958..e933091e 100644 --- a/gpu/kinfu/tools/record_tsdfvolume.cpp +++ b/gpu/kinfu/tools/record_tsdfvolume.cpp @@ -375,7 +375,7 @@ main (int argc, char* argv[]) // parse input cloud file pc::parse_argument (argc, argv, "-cf", cloud_file); - // pase output volume file + // parse output volume file pc::parse_argument (argc, argv, "-vf", volume_file); // parse options to extract and save cloud from volume diff --git a/gpu/kinfu/tools/tsdf_volume.h b/gpu/kinfu/tools/tsdf_volume.h index 50d97f9d..5cb742d5 100644 --- a/gpu/kinfu/tools/tsdf_volume.h +++ b/gpu/kinfu/tools/tsdf_volume.h @@ -225,9 +225,13 @@ EIGEN_MAKE_ALIGNED_OPERATOR_NEW //////////////////////////////////////////////////////////////////////////////////////// // Functionality - /** \brief Converts volume to cloud of TSDF values*/ + /** \brief Converts volume to cloud of TSDF values + * \param[ou] cloud - the output point cloud + * \param[in] step - the decimation step to use + */ void - convertToTsdfCloud (pcl::PointCloud::Ptr &cloud) const; + convertToTsdfCloud (pcl::PointCloud::Ptr &cloud, + const unsigned step = 2) const; /** \brief Converts the volume to a surface representation via a point cloud */ // void @@ -237,11 +241,11 @@ EIGEN_MAKE_ALIGNED_OPERATOR_NEW // template void // createFromCloud (const typename pcl::PointCloud::ConstPtr &cloud, const Intr &intr); - /** \brief Retunrs the 3D voxel coordinate */ + /** \brief Returns the 3D voxel coordinate */ template void getVoxelCoord (const PointT &point, Eigen::Vector3i &voxel_coord) const; - /** \brief Retunrs the 3D voxel coordinate and point offset wrt. to the voxel center (in mm) */ + /** \brief Returns the 3D voxel coordinate and point offset wrt. to the voxel center (in mm) */ template void getVoxelCoordAndOffset (const PointT &point, Eigen::Vector3i &voxel_coord, Eigen::Vector3f &offset) const; diff --git a/gpu/kinfu/tools/tsdf_volume.hpp b/gpu/kinfu/tools/tsdf_volume.hpp index a51d744e..ea4252a7 100644 --- a/gpu/kinfu/tools/tsdf_volume.hpp +++ b/gpu/kinfu/tools/tsdf_volume.hpp @@ -155,13 +155,13 @@ pcl::TSDFVolume::save (const std::string &filename, bool binary template void -pcl::TSDFVolume::convertToTsdfCloud (pcl::PointCloud::Ptr &cloud) const +pcl::TSDFVolume::convertToTsdfCloud (pcl::PointCloud::Ptr &cloud, + const unsigned step) const { int sx = header_.resolution(0); int sy = header_.resolution(1); int sz = header_.resolution(2); - const int step = 2; const int cloud_size = header_.getVolumeSize() / (step*step*step); cloud->clear(); @@ -204,7 +204,7 @@ pcl::TSDFVolume::getVoxelCoord (const PointT &point, Eigen::Vec } -/** \brief Retunrs the 3D voxel coordinate and point offset wrt. to the voxel center (in mm) */ +/** \brief Returns the 3D voxel coordinate and point offset wrt. to the voxel center (in mm) */ template template void pcl::TSDFVolume::getVoxelCoordAndOffset (const PointT &point, Eigen::Vector3i &coord, Eigen::Vector3f &offset) const diff --git a/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/color_volume.h b/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/color_volume.h index 6a6151d7..9f0f2f94 100644 --- a/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/color_volume.h +++ b/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/color_volume.h @@ -68,7 +68,7 @@ namespace pcl */ ColorVolume(const TsdfVolume& tsdf, int max_weight = -1); - /** \brief Desctructor */ + /** \brief Destructor */ ~ColorVolume(); /** \brief Resets color volume to uninitialized state */ diff --git a/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/cyclical_buffer.h b/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/cyclical_buffer.h index 2c8da278..09d13a6b 100644 --- a/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/cyclical_buffer.h +++ b/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/cyclical_buffer.h @@ -105,7 +105,7 @@ namespace pcl } /** \brief Check if shifting needs to be performed, returns true if so. - Shifting is considered needed if the target point is farther than distance_treshold_. + Shifting is considered needed if the target point is farther than distance_threshold_. The target point is located at distance_camera_point on the local Z axis of the camera. * \param[in] volume pointer to the TSDFVolume living in GPU * \param[in] cam_pose global pose of the camera in the world diff --git a/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/impl/world_model.hpp b/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/impl/world_model.hpp index d5727463..ea975d06 100644 --- a/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/impl/world_model.hpp +++ b/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/impl/world_model.hpp @@ -228,7 +228,7 @@ pcl::kinfuLS::WorldModel::getWorldAsCubes (const double size, std::vecto } else { - PCL_INFO ("Extracted cube was empty, skiping this one.\n"); + PCL_INFO ("Extracted cube was empty, skipping this one.\n"); } origin.z += cubeSide * step_increment; } diff --git a/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/kinfu.h b/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/kinfu.h index cdde4ae9..99403541 100644 --- a/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/kinfu.h +++ b/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/kinfu.h @@ -105,7 +105,7 @@ namespace pcl void setDepthIntrinsics (float fx, float fy, float cx = -1, float cy = -1); - /** \brief Sets initial camera pose relative to volume coordiante space + /** \brief Sets initial camera pose relative to volume coordinate space * \param[in] pose Initial camera pose */ void @@ -300,7 +300,7 @@ namespace pcl pcl::device::kinfuLS::Mat33& transform_out, float3& translation_out); /** \brief helper function that pre-process a raw detph map the kinect fusion algorithm. - * The raw depth map is first blured, eventually truncated, and downsampled for each pyramid level. + * The raw depth map is first blurred, eventually truncated, and downsampled for each pyramid level. * Then, vertex and normal maps are computed for each pyramid level. * \param[in] depth_raw the raw depth map to process * \param[in] cam_intrinsics intrinsics of the camera used to acquire the depth map diff --git a/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/marching_cubes.h b/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/marching_cubes.h index b3198f5b..b484d184 100644 --- a/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/marching_cubes.h +++ b/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/marching_cubes.h @@ -81,7 +81,7 @@ namespace pcl /** \brief Runs marching cubes triangulation. * \param[in] tsdf * \param[in] triangles_buffer Buffer for triangles. Its size determines max extracted triangles. If empty, it will be allocated with default size will be used. - * \return Array with triangles. Each 3 consequent poits belond to a single triangle. The returned array points to 'triangles_buffer' data. + * \return Array with triangles. Each 3 consequent points belong to a single triangle. The returned array points to 'triangles_buffer' data. */ DeviceArray run(const TsdfVolume& tsdf, DeviceArray& triangles_buffer); @@ -90,13 +90,13 @@ namespace pcl /** \brief Edge table for marching cubes */ DeviceArray edgeTable_; - /** \brief Number of vertextes table for marching cubes */ + /** \brief Number of vertices table for marching cubes */ DeviceArray numVertsTable_; /** \brief Triangles table for marching cubes */ DeviceArray triTable_; - /** \brief Temporary buffer used by marching cubes (first row stores occuped voxes id, second number of vetexes, third poits offsets */ + /** \brief Temporary buffer used by marching cubes (first row stores occupied voxel id, second number of vertices, third points offsets */ DeviceArray2D occupied_voxels_buffer_; }; } diff --git a/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/raycaster.h b/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/raycaster.h index 5af9398d..3375626e 100644 --- a/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/raycaster.h +++ b/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/raycaster.h @@ -88,7 +88,7 @@ namespace pcl void setIntrinsics(float fx = 525.f, float fy = 525.f, float cx = -1, float cy = -1); - /** \brief Runs raycasting algorithm from given camera pose. It writes results to internal fiels. + /** \brief Runs raycasting algorithm from given camera pose. It writes results to internal files. * \param[in] volume tsdf volume container * \param[in] camera_pose camera pose * \param buffer diff --git a/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/tsdf_volume.h b/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/tsdf_volume.h index e2e067e9..7d009726 100644 --- a/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/tsdf_volume.h +++ b/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/tsdf_volume.h @@ -112,7 +112,7 @@ EIGEN_MAKE_ALIGNED_OPERATOR_NEW */ TsdfVolume (const Eigen::Vector3i& resolution); - /** \brief Sets Tsdf volume size for each dimention + /** \brief Sets Tsdf volume size for each dimension * \param[in] size size of tsdf volume in meters */ void @@ -124,7 +124,7 @@ EIGEN_MAKE_ALIGNED_OPERATOR_NEW void setTsdfTruncDist (float distance); - /** \brief Returns tsdf volume container that point to data in GPU memroy */ + /** \brief Returns tsdf volume container that point to data in GPU memory */ DeviceArray2D data () const; @@ -243,9 +243,13 @@ EIGEN_MAKE_ALIGNED_OPERATOR_NEW return header_.getVolumeSize (); } - /** \brief Converts volume stored on host to cloud of TSDF values*/ + /** \brief Converts volume stored on host to cloud of TSDF values + * \param[ou] cloud - the output point cloud + * \param[in] step - the decimation step to use + */ void - convertToTsdfCloud (pcl::PointCloud::Ptr &cloud) const; + convertToTsdfCloud (pcl::PointCloud::Ptr &cloud, + const unsigned step = 2) const; /** \brief Returns the voxel grid resolution */ inline const Eigen::Vector3i & diff --git a/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/world_model.h b/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/world_model.h index 4ccd1b77..e0dff28a 100644 --- a/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/world_model.h +++ b/gpu/kinfu_large_scale/include/pcl/gpu/kinfu_large_scale/world_model.h @@ -57,7 +57,7 @@ namespace pcl /** \brief WorldModel maintains a 3D point cloud that can be queried and updated via helper functions.\n * The world is represented as a point cloud.\n * When new points are added to the world, we replace old ones by the newest ones. - * This is acheived by setting old points to nan (for speed) + * This is achieved by setting old points to nan (for speed) * \author Raphael Favier */ template @@ -103,7 +103,7 @@ namespace pcl void addSlice (const PointCloudPtr new_cloud); - /** \brief Retreive existing data from the world model, after a shift + /** \brief Retrieve existing data from the world model, after a shift * \param[in] previous_origin_x global origin of the cube on X axis, before the shift * \param[in] previous_origin_y global origin of the cube on Y axis, before the shift * \param[in] previous_origin_z global origin of the cube on Z axis, before the shift @@ -161,7 +161,7 @@ namespace pcl * \param[in] size the size of a 3D cube. * \param[out] cubes a vector of point clouds representing each cube (in their original world coordinates). * \param[out] transforms a vector containing the xyz position of each cube in world coordinates. - * \param[in] overlap optional overlap (in percent) between each cube (usefull to create overlapped meshes). + * \param[in] overlap optional overlap (in percent) between each cube (useful to create overlapped meshes). */ void getWorldAsCubes (double size, std::vector &cubes, std::vector > &transforms, double overlap = 0.0); diff --git a/gpu/kinfu_large_scale/src/cuda/extract.cu b/gpu/kinfu_large_scale/src/cuda/extract.cu index 72ed2134..d5136d7e 100644 --- a/gpu/kinfu_large_scale/src/cuda/extract.cu +++ b/gpu/kinfu_large_scale/src/cuda/extract.cu @@ -108,7 +108,11 @@ namespace pcl __shared__ int cta_buffer[CTA_SIZE]; #endif - #if __CUDA_ARCH__ >= 120 + #if CUDA_VERSION >= 9000 + if (__all_sync (__activemask (), x >= VOLUME_X) + || __all_sync (__activemask (), y >= VOLUME_Y)) + return; + #elif __CUDA_ARCH__ >= 120 if (__all (x >= VOLUME_X) || __all (y >= VOLUME_Y)) return; #else @@ -206,7 +210,11 @@ namespace pcl }/* if (x < VOLUME_X && y < VOLUME_Y) */ - #if __CUDA_ARCH__ >= 200 + #if CUDA_VERSION >= 9000 + int total_warp = __popc (__ballot_sync (__activemask (), local_count > 0)) + + __popc (__ballot_sync (__activemask (), local_count > 1)) + + __popc (__ballot_sync (__activemask (), local_count > 2)); + #elif __CUDA_ARCH__ >= 200 //not we fulfilled points array at current iteration int total_warp = __popc (__ballot (local_count > 0)) + __popc (__ballot (local_count > 1)) + __popc (__ballot (local_count > 2)); #else @@ -316,8 +324,15 @@ namespace pcl // local_count counts the number of zero crossing for the current thread. Now we need to merge this knowledge with the other threads // not we fulfilled points array at current iteration - int total_warp = __popc (__ballot (local_count > 0)) + __popc (__ballot (local_count > 1)) + __popc (__ballot (local_count > 2)); - + #if CUDA_VERSION >= 9000 + int total_warp = __popc (__ballot_sync (__activemask (), local_count > 0)) + + __popc (__ballot_sync (__activemask (), local_count > 1)) + + __popc (__ballot_sync (__activemask (), local_count > 2)); + #else + int total_warp = __popc (__ballot (local_count > 0)) + + __popc (__ballot (local_count > 1)) + + __popc (__ballot (local_count > 2)); + #endif if (total_warp > 0) ///more than 0 zero-crossings { diff --git a/gpu/kinfu_large_scale/src/cuda/maps.cu b/gpu/kinfu_large_scale/src/cuda/maps.cu index ef1f4927..a33e96ff 100644 --- a/gpu/kinfu_large_scale/src/cuda/maps.cu +++ b/gpu/kinfu_large_scale/src/cuda/maps.cu @@ -176,7 +176,7 @@ namespace pcl if (x < cols && y < rows) { - //vetexes + //vertices float3 vsrc, vdst = make_float3 (qnan, qnan, qnan); vsrc.x = vmap_src.ptr (y)[x]; diff --git a/gpu/kinfu_large_scale/src/cuda/marching_cubes.cu b/gpu/kinfu_large_scale/src/cuda/marching_cubes.cu index 0ecec1df..2c84b4f9 100644 --- a/gpu/kinfu_large_scale/src/cuda/marching_cubes.cu +++ b/gpu/kinfu_large_scale/src/cuda/marching_cubes.cu @@ -146,8 +146,14 @@ namespace pcl int x = threadIdx.x + blockIdx.x * CTA_SIZE_X; int y = threadIdx.y + blockIdx.y * CTA_SIZE_Y; + #if CUDA_VERSION >= 9000 + if (__all_sync (__activemask (), x >= VOLUME_X) + || __all_sync (__activemask (), y >= VOLUME_Y)) + return; + #else if (__all (x >= VOLUME_X) || __all (y >= VOLUME_Y)) return; + #endif int ftid = Block::flattenedThreadId (); int warp_id = Warp::id(); @@ -167,7 +173,11 @@ namespace pcl numVerts = (cubeindex == 0 || cubeindex == 255) ? 0 : tex1Dfetch (numVertsTex, cubeindex); } + #if CUDA_VERSION >= 9000 + int total = __popc (__ballot_sync (__activemask (), numVerts > 0)); + #else int total = __popc (__ballot (numVerts > 0)); + #endif if (total == 0) continue; @@ -178,7 +188,11 @@ namespace pcl } int old_global_voxels_count = warps_buffer[warp_id]; + #if CUDA_VERSION >= 9000 + int offs = Warp::binaryExclScan (__ballot_sync (__activemask (), numVerts > 0)); + #else int offs = Warp::binaryExclScan (__ballot (numVerts > 0)); + #endif if (old_global_voxels_count + offs < max_size && numVerts > 0) { diff --git a/gpu/kinfu_large_scale/src/cuda/tsdf_volume.cu b/gpu/kinfu_large_scale/src/cuda/tsdf_volume.cu index 7dbbaff9..ebcca8dc 100644 --- a/gpu/kinfu_large_scale/src/cuda/tsdf_volume.cu +++ b/gpu/kinfu_large_scale/src/cuda/tsdf_volume.cu @@ -103,7 +103,7 @@ namespace pcl #pragma unroll for(int z = 0; z < buffer.voxels_size.z; ++z, pos+=z_step) { - ///If we went outside of the memory, make sure we go back to the begining of it + ///If we went outside of the memory, make sure we go back to the beginning of it if(pos > buffer.tsdf_memory_end) pos = pos - size; @@ -141,7 +141,7 @@ namespace pcl #pragma unroll for(int z = 0; z < nbSteps; ++z, pos+=z_step) { - ///If we went outside of the memory, make sure we go back to the begining of it + ///If we went outside of the memory, make sure we go back to the beginning of it if(pos > buffer.tsdf_memory_end) pos = pos - size; @@ -224,7 +224,7 @@ namespace pcl { float3 v_g = getVoxelGCoo (x, y, z); //3 // p - //tranform to curr cam coo space + //transform to curr cam coo space float3 v = Rcurr_inv * (v_g - tcurr); //4 int2 coo; //project to current cam diff --git a/gpu/kinfu_large_scale/src/cuda/utils.hpp b/gpu/kinfu_large_scale/src/cuda/utils.hpp index 6daf6f15..b49e41ef 100644 --- a/gpu/kinfu_large_scale/src/cuda/utils.hpp +++ b/gpu/kinfu_large_scale/src/cuda/utils.hpp @@ -38,8 +38,8 @@ #ifndef PCL_GPU_KINFU_CUDA_UTILS_HPP_ #define PCL_GPU_KINFU_CUDA_UTILS_HPP_ -//#include +#include namespace pcl { @@ -185,7 +185,7 @@ namespace pcl if (roots.x >= roots.y) swap (roots.x, roots.y); } - if (roots.x <= 0) // eigenval for symetric positive semi-definite matrix can not be negative! Set it to 0 + if (roots.x <= 0) // eigenval for symmetric positive semi-definite matrix can not be negative! Set it to 0 computeRoots2 (c2, c1, roots); } } @@ -605,9 +605,12 @@ namespace pcl static __forceinline__ __device__ int Ballot(int predicate, volatile int* cta_buffer) { - #if __CUDA_ARCH__ >= 200 + #if CUDA_VERSION >= 9000 (void)cta_buffer; - return __ballot(predicate); + return __ballot_sync (__activemask (), predicate); + #elif __CUDA_ARCH__ >= 200 + (void)cta_buffer; + return __ballot (predicate); #else int tid = Block::flattenedThreadId(); cta_buffer[tid] = predicate ? (1 << (tid & 31)) : 0; @@ -618,9 +621,12 @@ namespace pcl static __forceinline__ __device__ bool All(int predicate, volatile int* cta_buffer) { - #if __CUDA_ARCH__ >= 200 + #if CUDA_VERSION >= 9000 + (void)cta_buffer; + return __all_sync (__activemask (), predicate); + #elif __CUDA_ARCH__ >= 200 (void)cta_buffer; - return __all(predicate); + return __all (predicate); #else int tid = Block::flattenedThreadId(); cta_buffer[tid] = predicate ? 1 : 0; diff --git a/gpu/kinfu_large_scale/src/internal.h b/gpu/kinfu_large_scale/src/internal.h index 8c1e92bd..eaa0afe2 100644 --- a/gpu/kinfu_large_scale/src/internal.h +++ b/gpu/kinfu_large_scale/src/internal.h @@ -60,8 +60,8 @@ namespace pcl /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Maps - /** \brief Perfoms bilateral filtering of disparity map - * \param[in] src soruce map + /** \brief Performs bilateral filtering of disparity map + * \param[in] src source map * \param[out] dst output map */ void @@ -96,7 +96,7 @@ namespace pcl void computeNormalsEigen (const MapArr& vmap, MapArr& nmap); - /** \brief Performs affine tranform of vertex and normal maps + /** \brief Performs affine transform of vertex and normal maps * \param[in] vmap_src source vertex map * \param[in] nmap_src source vertex map * \param[in] Rmat Rotation mat @@ -117,7 +117,7 @@ namespace pcl /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ICP - /** \brief (now it's exra code) Computes corespondances map + /** \brief (now it's extra code) Computes corespondances map * \param[in] vmap_g_curr current vertex map in global coo space * \param[in] nmap_g_curr current normals map in global coo space * \param[in] Rprev_inv inverse camera rotation at previous pose @@ -133,7 +133,7 @@ namespace pcl findCoresp (const MapArr& vmap_g_curr, const MapArr& nmap_g_curr, const Mat33& Rprev_inv, const float3& tprev, const Intr& intr, const MapArr& vmap_g_prev, const MapArr& nmap_g_prev, float distThres, float angleThres, PtrStepSz coresp); - /** \brief (now it's exra code) Computation Ax=b for ICP iteration + /** \brief (now it's extra code) Computation Ax=b for ICP iteration * \param[in] v_dst destination vertex map (previous frame cloud) * \param[in] n_dst destination normal map (previous frame normals) * \param[in] v_src source normal map (current frame cloud) @@ -337,7 +337,7 @@ namespace pcl /** \brief Perform point cloud extraction from tsdf volume * \param[in] volume tsdf volume * \param[in] volume_size size of the volume - * \param[out] output buffer large enought to store point cloud + * \param[out] output buffer large enough to store point cloud * \return number of point stored to passed buffer */ PCL_EXPORTS size_t @@ -350,14 +350,14 @@ namespace pcl * \param[in] shiftX Offset in indices that will be cleared from the TSDF volume. The clearing start from buffer.OriginX and stops in OriginX + shiftX * \param[in] shiftY Offset in indices that will be cleared from the TSDF volume. The clearing start from buffer.OriginY and stops in OriginY + shiftY * \param[in] shiftZ Offset in indices that will be cleared from the TSDF volume. The clearing start from buffer.OriginZ and stops in OriginZ + shiftZ - * \param[out] output_xyz buffer large enought to store point cloud xyz values - * \param[out] output_intensities buffer large enought to store point cloud intensity values + * \param[out] output_xyz buffer large enough to store point cloud xyz values + * \param[out] output_intensities buffer large enough to store point cloud intensity values * \return number of point stored to passed buffer */ PCL_EXPORTS size_t extractSliceAsCloud (const PtrStep& volume, const float3& volume_size, const pcl::gpu::kinfuLS::tsdf_buffer* buffer, const int shiftX, const int shiftY, const int shiftZ, PtrSz output_xyz, PtrSz output_intensities); - /** \brief Performs normals computation for given poins using tsdf volume + /** \brief Performs normals computation for given points using tsdf volume * \param[in] volume tsdf volume * \param[in] volume_size volume size * \param[in] input points where normals are computed @@ -429,24 +429,24 @@ namespace pcl void unbindTextures(); - /** \brief Scans tsdf volume and retrieves occuped voxes + /** \brief Scans tsdf volume and retrieves occupied voxels * \param[in] volume tsdf volume - * \param[out] occupied_voxels buffer for occuped voxels. The function fulfills first row with voxel ids and second row with number of vertextes. + * \param[out] occupied_voxels buffer for occupied voxels. The function fulfills first row with voxel ids and second row with number of vertices. * \return number of voxels in the buffer */ int getOccupiedVoxels(const PtrStep& volume, DeviceArray2D& occupied_voxels); - /** \brief Computes total number of vertexes for all voxels and offsets of vertexes in final triangle array - * \param[out] occupied_voxels buffer with occuped voxels. The function fulfills 3nd only with offsets - * \return total number of vertexes + /** \brief Computes total number of vertices for all voxels and offsets of vertices in final triangle array + * \param[out] occupied_voxels buffer with occupied voxels. The function fulfills 3nd only with offsets + * \return total number of vertices */ int computeOffsetsAndTotalVertexes(DeviceArray2D& occupied_voxels); /** \brief Generates final triangle array * \param[in] volume tsdf volume - * \param[in] occupied_voxels occuped voxel ids (first row), number of vertexes(second row), offsets(third row). + * \param[in] occupied_voxels occupied voxel ids (first row), number of vertices(second row), offsets(third row). * \param[in] volume_size volume size in meters * \param[out] output triangle array */ diff --git a/gpu/kinfu_large_scale/src/kinfu.cpp b/gpu/kinfu_large_scale/src/kinfu.cpp index 33514258..6bd3b7d4 100644 --- a/gpu/kinfu_large_scale/src/kinfu.cpp +++ b/gpu/kinfu_large_scale/src/kinfu.cpp @@ -295,7 +295,7 @@ pcl::gpu::kinfuLS::KinfuTracker::allocateBufffers (int rows, int cols) coresps_[i].create (pyr_rows, pyr_cols); } depthRawScaled_.create (rows, cols); - // see estimate tranform for the magic numbers + // see estimate transform for the magic numbers int r = (int)ceil ( ((float)rows) / ESTIMATE_COMBINED_CUDA_GRID_Y ); int c = (int)ceil ( ((float)cols) / ESTIMATE_COMBINED_CUDA_GRID_X ); gbuf_.create (27, r * c); @@ -545,7 +545,7 @@ pcl::gpu::kinfuLS::KinfuTracker::performPairWiseICP(const Intr cam_intrinsics, M } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // since raw depthmaps are quite noisy, we make sure the estimated transform is big enought to be taken into account + // since raw depthmaps are quite noisy, we make sure the estimated transform is big enough to be taken into account float rnorm = rodrigues2(current_rotation).norm(); float tnorm = (current_translation).norm(); const float alpha = 1.f; @@ -582,7 +582,7 @@ pcl::gpu::kinfuLS::KinfuTracker::operator() (const DepthMap& depth_raw) /////////////////////////////////////////////////////////////////////////////////////////// // Initialization at first frame - if (global_time_ == 0) // this is the frist frame, the tsdf volume needs to be initialized + if (global_time_ == 0) // this is the first frame, the tsdf volume needs to be initialized { // Initial rotation Matrix3frm initial_cam_rot = rmats_[0]; // [Ri|ti] - pos of camera @@ -667,7 +667,7 @@ pcl::gpu::kinfuLS::KinfuTracker::operator() (const DepthMap& depth_raw) device_current_translation_local -= getCyclicalBufferStructure()->origin_metric; // translation (local translation = global translation - origin of cube) /////////////////////////////////////////////////////////////////////////////////////////// - // Integration check - We do not integrate volume if camera does not move far enought. + // Integration check - We do not integrate volume if camera does not move far enough. { float rnorm = rodrigues2(current_global_rotation.inverse() * last_known_global_rotation).norm(); float tnorm = (current_global_translation - last_known_global_translation).norm(); diff --git a/gpu/kinfu_large_scale/src/tsdf_volume.cpp b/gpu/kinfu_large_scale/src/tsdf_volume.cpp index 6198a4b3..73e25120 100644 --- a/gpu/kinfu_large_scale/src/tsdf_volume.cpp +++ b/gpu/kinfu_large_scale/src/tsdf_volume.cpp @@ -354,13 +354,13 @@ pcl::gpu::kinfuLS::TsdfVolume::fetchNormals (const DeviceArray& cloud /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void -pcl::gpu::kinfuLS::TsdfVolume::convertToTsdfCloud (pcl::PointCloud::Ptr &cloud) const +pcl::gpu::kinfuLS::TsdfVolume::convertToTsdfCloud ( pcl::PointCloud::Ptr &cloud, + const unsigned step) const { int sx = header_.resolution(0); int sy = header_.resolution(1); int sz = header_.resolution(2); - const int step = 2; const int cloud_size = static_cast (header_.getVolumeSize() / (step*step*step)); cloud->clear(); diff --git a/gpu/kinfu_large_scale/tools/kinfu_app_sim.cpp b/gpu/kinfu_large_scale/tools/kinfu_app_sim.cpp index 730d4668..a6c7f34e 100644 --- a/gpu/kinfu_large_scale/tools/kinfu_app_sim.cpp +++ b/gpu/kinfu_large_scale/tools/kinfu_app_sim.cpp @@ -394,7 +394,7 @@ display_tic_toc (vector &tic_toc,const string &fun_name) void capture (Eigen::Isometry3d pose_in,unsigned short* depth_buffer_mm,const uint8_t* color_buffer)//, string point_cloud_fname) { - // No reference image - but this is kept for compatability with range_test_v2: + // No reference image - but this is kept for compatibility with range_test_v2: float* reference = new float[range_likelihood_->getRowHeight() * range_likelihood_->getColWidth()]; //const float* depth_buffer = range_likelihood_->getDepthBuffer(); // Copy one image from our last as a reference. @@ -1076,7 +1076,7 @@ struct KinFuApp PtrStepSz rgb24_sim = PtrStepSz(height, width, color_buf_, width); tic_toc.push_back (getTime ()); - if (1==0){ // live capture - probably doesnt work anymore, left in here for comparison: + if (1==0){ // live capture - probably doesn't work anymore, left in here for comparison: bool has_frame = evaluation_ptr_ ? evaluation_ptr_->grab(i, depth) : capture_.grab (depth, rgb24); if (!has_frame) { @@ -1386,7 +1386,7 @@ print_cli_help () cout << " --registration, -r : enable registration mode" << endl; cout << " --integrate-colors, -ic : enable color integration mode ( allows to get cloud with colors )" << endl; cout << " -volume_suze : define integration volume size" << endl; - cout << " -dev , -oni : select depth source. Default will be selected if not specified" << endl; + cout << " -dev , -oni : select depth source. Default will be selected if not specified" << endl; cout << ""; cout << " For RGBD benchmark (Requires OpenCV):" << endl; cout << " -eval [-match_file ]" << endl; diff --git a/gpu/kinfu_large_scale/tools/record_maps_rgb.cpp b/gpu/kinfu_large_scale/tools/record_maps_rgb.cpp index 806ddbc2..5b16a205 100644 --- a/gpu/kinfu_large_scale/tools/record_maps_rgb.cpp +++ b/gpu/kinfu_large_scale/tools/record_maps_rgb.cpp @@ -284,7 +284,7 @@ receiveAndProcess () { boost::mutex::scoped_lock io_lock (io_mutex); - PCL_INFO ("Writing remaing %d maps in the buffer to disk...\n", buff.getSize ()); + PCL_INFO ("Writing remaining %d maps in the buffer to disk...\n", buff.getSize ()); } while (!buff.isEmpty ()) { diff --git a/gpu/kinfu_large_scale/tools/record_tsdfvolume.cpp b/gpu/kinfu_large_scale/tools/record_tsdfvolume.cpp index a42a92c6..92ca4741 100644 --- a/gpu/kinfu_large_scale/tools/record_tsdfvolume.cpp +++ b/gpu/kinfu_large_scale/tools/record_tsdfvolume.cpp @@ -373,7 +373,7 @@ main (int argc, char* argv[]) // parse input cloud file pc::parse_argument (argc, argv, "-cf", cloud_file); - // pase output volume file + // parse output volume file pc::parse_argument (argc, argv, "-vf", volume_file); // parse options to extract and save cloud from volume diff --git a/gpu/kinfu_large_scale/tools/standalone_texture_mapping.cpp b/gpu/kinfu_large_scale/tools/standalone_texture_mapping.cpp index 3450e4d5..13554db4 100644 --- a/gpu/kinfu_large_scale/tools/standalone_texture_mapping.cpp +++ b/gpu/kinfu_large_scale/tools/standalone_texture_mapping.cpp @@ -201,7 +201,7 @@ saveOBJFile (const std::string &file_name, int f_idx = 0; // int idx_vt =0; - PCL_INFO ("Writting faces...\n"); + PCL_INFO ("Writing faces...\n"); for (int m = 0; m < nr_meshes; ++m) { if (m > 0) @@ -239,10 +239,10 @@ saveOBJFile (const std::string &file_name, PCL_INFO ("Closing obj file\n"); fs.close (); - /* Write material defination for OBJ file*/ + /* Write material definition for OBJ file*/ // Open file PCL_INFO ("Writing material files\n"); - //dont do it if no material to write + //don't do it if no material to write if(tex_mesh.tex_materials.size() ==0) return (0); @@ -363,7 +363,7 @@ std::ifstream& GotoLine(std::ifstream& file, unsigned int num) return (file); } -/** \brief Helper function that reads a camera file outputed by Kinfu */ +/** \brief Helper function that reads a camera file outputted by Kinfu */ bool readCamPoseFile(std::string filename, pcl::TextureMapping::Camera &cam) { ifstream myReadFile; diff --git a/gpu/octree/include/pcl/gpu/octree/octree.hpp b/gpu/octree/include/pcl/gpu/octree/octree.hpp index c12c0e6c..e6ea1229 100644 --- a/gpu/octree/include/pcl/gpu/octree/octree.hpp +++ b/gpu/octree/include/pcl/gpu/octree/octree.hpp @@ -49,7 +49,7 @@ namespace pcl namespace gpu { /** - * \brief Octree implementation on GPU. It suppors parallel building and paralel batch search as well . + * \brief Octree implementation on GPU. It suppors parallel building and parallel batch search as well . * \author Anaoly Baksheev, Itseez, myname.mysurname@mycompany.com */ @@ -95,10 +95,10 @@ namespace pcl /** \brief Returns true if tree has been built */ bool isBuilt(); - /** \brief Downloads Octree from GPU to search using CPU fucntion. It use usefull for signle (not-batch) search */ + /** \brief Downloads Octree from GPU to search using CPU function. It use useful for single (not-batch) search */ void internalDownload(); - /** \brief Performs search of all points wihtin given radius on CPU. It call \a internalDownload if nessesary + /** \brief Performs search of all points within given radius on CPU. It call \a internalDownload if necessary * \param[in] center center of sphere * \param[in] radius radious of sphere * \param[out] out indeces of points within give sphere @@ -106,7 +106,7 @@ namespace pcl */ void radiusSearchHost(const PointType& center, float radius, std::vector& out, int max_nn = INT_MAX); - /** \brief Performs approximate neares neighbor search on CPU. It call \a internalDownload if nessesary + /** \brief Performs approximate nearest neighbor search on CPU. It call \a internalDownload if necessary * \param[in] query 3D point for which neighbour is be fetched * \param[out] out_index neighbour index * \param[out] sqr_dist square distance to the neighbour returned @@ -117,7 +117,7 @@ namespace pcl * \param[in] centers array of centers * \param[in] radius radius for all queries * \param[in] max_results max number of returned points for each querey - * \param[out] result results packed to signle array + * \param[out] result results packed to single array */ void radiusSearch(const Queries& centers, float radius, int max_results, NeighborIndices& result) const; @@ -125,7 +125,7 @@ namespace pcl * \param[in] centers array of centers * \param[in] radiuses array of radiuses * \param[in] max_results max number of returned points for each querey - * \param[out] result results packed to signle array + * \param[out] result results packed to single array */ void radiusSearch(const Queries& centers, const Radiuses& radiuses, int max_results, NeighborIndices& result) const; @@ -134,7 +134,7 @@ namespace pcl * \param[in] indices indices for centers array (only for these points search is performed) * \param[in] radius radius for all queries * \param[in] max_results max number of returned points for each querey - * \param[out] result results packed to signle array + * \param[out] result results packed to single array */ void radiusSearch(const Queries& centers, const Indices& indices, float radius, int max_results, NeighborIndices& result) const; @@ -146,7 +146,7 @@ namespace pcl /** \brief Batch exact k-nearest search on GPU for k == 1 only! * \param[in] queries array of centers - * \param[in] k nubmer of neighbors (only k == 1 is supported) + * \param[in] k number of neighbors (only k == 1 is supported) * \param[out] results array of results */ void nearestKSearchBatch(const Queries& queries, int k, NeighborIndices& results) const; diff --git a/gpu/octree/src/cuda/octree_builder.cu b/gpu/octree/src/cuda/octree_builder.cu index e5be567a..b0b877f1 100644 --- a/gpu/octree/src/cuda/octree_builder.cu +++ b/gpu/octree/src/cuda/octree_builder.cu @@ -186,7 +186,7 @@ namespace pcl __device__ __forceinline__ void operator()() const { - //32 is a perfomance penalty step for search + //32 is a performance penalty step for search Static<(max_points_per_leaf % 32) == 0>::check(); if (threadIdx.x == 0) diff --git a/gpu/octree/src/cuda/octree_host.cu b/gpu/octree/src/cuda/octree_host.cu index 90472161..aba137f8 100644 --- a/gpu/octree/src/cuda/octree_host.cu +++ b/gpu/octree/src/cuda/octree_host.cu @@ -89,14 +89,14 @@ void pcl::device::OctreeImpl::internalDownload() namespace { - int getBitsNum(int interger) + int getBitsNum(int integer) { int count = 0; - while(interger > 0) + while(integer > 0) { - if (interger & 1) + if (integer & 1) ++count; - interger>>=1; + integer>>=1; } return count; } diff --git a/gpu/octree/src/cuda/octree_iterator.hpp b/gpu/octree/src/cuda/octree_iterator.hpp index d490e1ad..86753c26 100644 --- a/gpu/octree/src/cuda/octree_iterator.hpp +++ b/gpu/octree/src/cuda/octree_iterator.hpp @@ -89,20 +89,20 @@ namespace pcl { int level; int node_idx; - int lenght; + int length; const OctreeGlobalWithBox& octree; __device__ __forceinline__ OctreeIteratorDeviceNS(const OctreeGlobalWithBox& octree_arg) : octree(octree_arg) { node_idx = 0; level = 0; - lenght = 1; + length = 1; } __device__ __forceinline__ void gotoNextLevel(int first, int len) { node_idx = first; - lenght = len; + length = len; ++level; } @@ -116,9 +116,9 @@ namespace pcl #if 1 while(level >= 0) { - if (lenght > 1) + if (length > 1) { - lenght--; + length--; node_idx++; break; } @@ -143,7 +143,7 @@ namespace pcl int pos = node_idx - parent_first; - lenght = parent_len - pos; + length = parent_len - pos; } #endif } diff --git a/gpu/octree/src/utils/priority_octree_iterator.hpp b/gpu/octree/src/utils/priority_octree_iterator.hpp index 9c0379ae..bcd616c6 100644 --- a/gpu/octree/src/utils/priority_octree_iterator.hpp +++ b/gpu/octree/src/utils/priority_octree_iterator.hpp @@ -45,20 +45,20 @@ namespace pcl { int level; int node_idx; - int lenght; + int length; const OctreeGlobalWithBox& octree; __device__ __forceinline__ OctreePriorityIteratorDevice(const OctreeGlobalWithBox& octree_arg) : octree(octree_arg) { node_idx = 0; level = 0; - lenght = 1; + length = 1; } __device__ __forceinline__ void gotoNextLevel(int first, int len) { node_idx = first; - lenght = len; + length = len; ++level; } @@ -72,9 +72,9 @@ namespace pcl #if 1 while(level >= 0) { - if (lenght > 1) + if (length > 1) { - lenght--; + length--; node_idx++; break; } @@ -99,7 +99,7 @@ namespace pcl int pos = node_idx - parent_first; - lenght = parent_len - pos; + length = parent_len - pos; } #endif } diff --git a/gpu/octree/test/perfomance.cpp b/gpu/octree/test/perfomance.cpp index e8bc6f3e..fcb3ca77 100644 --- a/gpu/octree/test/perfomance.cpp +++ b/gpu/octree/test/perfomance.cpp @@ -69,8 +69,8 @@ using pcl::ScopeTime; #include "opencv2/contrib/contrib.hpp" #endif -//TEST(PCL_OctreeGPU, DISABLED_perfomance) -TEST(PCL_OctreeGPU, perfomance) +//TEST(PCL_OctreeGPU, DISABLED_performance) +TEST(PCL_OctreeGPU, performance) { DataGenerator data; data.data_size = 871000; @@ -108,7 +108,7 @@ TEST(PCL_OctreeGPU, perfomance) cout << "[!] Host octree resolution: " << host_octree_resolution << endl << endl; - cout << "====== Build perfomance =====" << endl; + cout << "====== Build performance =====" << endl; // build device octree pcl::gpu::Octree octree_device; octree_device.setCloud(cloud_device); @@ -142,7 +142,7 @@ TEST(PCL_OctreeGPU, perfomance) } #endif - //// Radius search perfomance /// + //// Radius search performance /// const int max_answers = 500; float dist; diff --git a/gpu/octree/test/test_host_radius_search.cpp b/gpu/octree/test/test_host_radius_search.cpp index f38ee275..2654db3d 100644 --- a/gpu/octree/test/test_host_radius_search.cpp +++ b/gpu/octree/test/test_host_radius_search.cpp @@ -110,7 +110,7 @@ TEST(PCL_OctreeGPU, hostRadiusSearch) for(size_t i = 0; i < data.tests_num; ++i) { - //search host on octree tha was built on device + //search host on octree that was built on device vector results_host_gpu; //host search octree_device.radiusSearchHost(data.queries[i], data.radiuses[i], results_host_gpu); diff --git a/gpu/people/CMakeLists.txt b/gpu/people/CMakeLists.txt index f1538338..89b19cc1 100644 --- a/gpu/people/CMakeLists.txt +++ b/gpu/people/CMakeLists.txt @@ -20,10 +20,19 @@ if(build) find_cuda_helper_libs(npp) else() find_cuda_helper_libs(nppc) - find_cuda_helper_libs(nppi) find_cuda_helper_libs(npps) + if(${CUDA_VERSION} VERSION_GREATER_EQUAL "9.0") + find_cuda_helper_libs(nppim) + find_cuda_helper_libs(nppidei) + else() + find_cuda_helper_libs(nppi) + endif() - set(CUDA_npp_LIBRARY ${CUDA_nppc_LIBRARY} ${CUDA_nppi_LIBRARY} ${CUDA_npps_LIBRARY} CACHE STRING "npp library") + if(${CUDA_VERSION} VERSION_GREATER_EQUAL "9.0") + set(CUDA_npp_LIBRARY ${CUDA_nppc_LIBRARY} ${CUDA_nppim_LIBRARY} ${CUDA_nppidei_LIBRARY} ${CUDA_npps_LIBRARY} CACHE STRING "npp library") + else() + set(CUDA_npp_LIBRARY ${CUDA_nppc_LIBRARY} ${CUDA_nppi_LIBRARY} ${CUDA_npps_LIBRARY} CACHE STRING "npp library") + endif() endif() #Label_skeleton diff --git a/gpu/people/include/pcl/gpu/people/label_blob2.h b/gpu/people/include/pcl/gpu/people/label_blob2.h index b24f6d77..e101ea18 100644 --- a/gpu/people/include/pcl/gpu/people/label_blob2.h +++ b/gpu/people/include/pcl/gpu/people/label_blob2.h @@ -52,7 +52,7 @@ namespace pcl namespace people { /** - * @brief This structure containts all parameters to describe blobs and their parent/child relations + * @brief This structure contains all parameters to describe blobs and their parent/child relations * @todo: clean this out in the end, perhaps place the children in a separate struct */ struct Blob2 diff --git a/gpu/people/include/pcl/gpu/people/label_common.h b/gpu/people/include/pcl/gpu/people/label_common.h index 76fb6e29..02f65c36 100644 --- a/gpu/people/include/pcl/gpu/people/label_common.h +++ b/gpu/people/include/pcl/gpu/people/label_common.h @@ -151,7 +151,7 @@ namespace pcl }; /** - * @brief This LUT contains the ideal lenght between this part and his children + * @brief This LUT contains the ideal length between this part and his children **/ static const float LUT_ideal_length[][4] = { @@ -183,7 +183,7 @@ namespace pcl }; /** - * @brief This LUT contains the max lenght between this part and his children + * @brief This LUT contains the max length between this part and his children **/ static const float LUT_max_length_offset[][4] = { diff --git a/gpu/people/include/pcl/gpu/people/label_segment.h b/gpu/people/include/pcl/gpu/people/label_segment.h index a1138865..74839bb6 100644 --- a/gpu/people/include/pcl/gpu/people/label_segment.h +++ b/gpu/people/include/pcl/gpu/people/label_segment.h @@ -77,7 +77,7 @@ namespace pcl * \param[in] dmap the cvMat with the depths, must be CV_16U in mm * \param[out] lmap_out the smoothed output labelmap as cvMat * \param[in] patch_size make the patch size for smoothing - * \param[in] depthThres the z-distance thresshold + * \param[in] depthThres the z-distance threshold * \todo add a Gaussian contribution function to depth and vote **/ //inline void smoothLabelImage ( cv::Mat& lmap_in, diff --git a/gpu/people/include/pcl/gpu/people/label_tree.h b/gpu/people/include/pcl/gpu/people/label_tree.h index 677d569e..01f6e4e4 100644 --- a/gpu/people/include/pcl/gpu/people/label_tree.h +++ b/gpu/people/include/pcl/gpu/people/label_tree.h @@ -70,7 +70,7 @@ namespace pcl namespace people { /** - * @brief This structure containts all parameters to describe the segmented tree + * @brief This structure contains all parameters to describe the segmented tree */ struct Tree2 { @@ -222,7 +222,7 @@ namespace pcl * @param[in] parent_label this is the part label that indicates the row * @param[in] child_label this is the part label that indicates the childs needed to be investigated * @param[in] child_number the number of this child in the parent, some parents have multiple childs - * @return zero if successfull + * @return zero if successful * @todo once we have good evaluation function reconsider best_value **/ inline int @@ -281,7 +281,7 @@ namespace pcl * @param[in] child_label this is the part label that indicates the childs needed to be investigated * @param[in] child_number the number of this child in the parent, some parents have multiple childs * @param person_attribs - * @return zero if successfull + * @return zero if successful * @todo once we have good evaluation function reconsider best_value **/ inline int diff --git a/gpu/people/include/pcl/gpu/people/person_attribs.h b/gpu/people/include/pcl/gpu/people/person_attribs.h index 95673cf7..05fe8199 100644 --- a/gpu/people/include/pcl/gpu/people/person_attribs.h +++ b/gpu/people/include/pcl/gpu/people/person_attribs.h @@ -25,7 +25,7 @@ namespace pcl /** * \brief Read XML configuration file for a specific person * \param[in] is input stream of file - * \return 0 when successfull, -1 when an error occured, datastructure might become corrupted in the process + * \return 0 when successful, -1 when an error occurred, datastructure might become corrupted in the process **/ int readPersonXMLConfig (std::istream& is); diff --git a/gpu/people/src/cuda/smooth.cu b/gpu/people/src/cuda/smooth.cu index 79f76c52..ae7ed9d4 100644 --- a/gpu/people/src/cuda/smooth.cu +++ b/gpu/people/src/cuda/smooth.cu @@ -142,7 +142,7 @@ void pcl::device::smoothLabelImage(const Labels& src, const Depth& depth, Labels if (num_parts <= 32) pcl::device::smoothKernel<32><<>>(src, depth, dst, patch_size, depthThres, num_parts); else - throw std::exception(); //should instanciate another smoothKernel + throw std::exception(); //should instantiate another smoothKernel cudaSafeCall( cudaGetLastError() ); cudaSafeCall( cudaDeviceSynchronize() ); diff --git a/gpu/people/tools/people_app.cpp b/gpu/people/tools/people_app.cpp index be33ac04..0a9e0214 100644 --- a/gpu/people/tools/people_app.cpp +++ b/gpu/people/tools/people_app.cpp @@ -129,7 +129,7 @@ savePNGFile(const std::string& filename, const pcl::gpu::DeviceArray2D& arr) template void savePNGFile (const std::string& filename, const pcl::PointCloud& cloud) { - pcl::io::savePNGFile(filename, cloud); + pcl::io::savePNGFile(filename, cloud, "rgb"); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/gpu/people/tools/people_pcd_prob.cpp b/gpu/people/tools/people_pcd_prob.cpp index b5ef9c25..8fe5484e 100644 --- a/gpu/people/tools/people_pcd_prob.cpp +++ b/gpu/people/tools/people_pcd_prob.cpp @@ -108,7 +108,7 @@ savePNGFile(const std::string& filename, const pcl::gpu::DeviceArray2D& arr) template void savePNGFile (const std::string& filename, const pcl::PointCloud& cloud) { - pcl::io::savePNGFile(filename, cloud); + pcl::io::savePNGFile(filename, cloud, "rgb"); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/gpu/surface/src/cuda/convex_hull.cu b/gpu/surface/src/cuda/convex_hull.cu index d21f3f36..f26e6138 100644 --- a/gpu/surface/src/cuda/convex_hull.cu +++ b/gpu/surface/src/cuda/convex_hull.cu @@ -467,9 +467,14 @@ namespace pcl { int idx = threadIdx.x + blockIdx.x * blockDim.x; - if (__all(idx >= facet_count)) +#if CUDA_VERSION >= 9000 + if (__all_sync (__activemask (), idx >= facet_count)) + return; +#else + if (__all (idx >= facet_count)) return; - +#endif + int empty = 0; if(idx < facet_count) @@ -492,10 +497,18 @@ namespace pcl empty = 1; } - int total = __popc(__ballot(empty)); +#if CUDA_VERSION >= 9000 + int total = __popc (__ballot_sync (__activemask (), empty)); +#else + int total = __popc (__ballot (empty)); +#endif if (total > 0) { - int offset = Warp::binaryExclScan(__ballot(empty)); +#if CUDA_VERSION >= 9000 + int offset = Warp::binaryExclScan (__ballot_sync (__activemask (), empty)); +#else + int offset = Warp::binaryExclScan (__ballot (empty)); +#endif volatile __shared__ int wapr_buffer[WARPS]; diff --git a/gpu/surface/src/internal.h b/gpu/surface/src/internal.h index 6f4fa934..2a1e7b29 100644 --- a/gpu/surface/src/internal.h +++ b/gpu/surface/src/internal.h @@ -66,7 +66,7 @@ namespace pcl public: FacetStream(size_t buffer_size); - // indeces: in each col indeces of vertexes for sigle facet + // indeces: in each col indeces of vertexes for single facet DeviceArray2D verts_inds; DeviceArray head_points; diff --git a/gpu/utils/CMakeLists.txt b/gpu/utils/CMakeLists.txt index 4c515bbb..b3bf676b 100644 --- a/gpu/utils/CMakeLists.txt +++ b/gpu/utils/CMakeLists.txt @@ -26,5 +26,6 @@ if(build) PCL_MAKE_PKGCONFIG("${LIB_NAME}" "${SUBSYS_NAME}" "${SUBSYS_DESC}" "${SUBSYS_DEPS}" "${EXT_DEPS}" "" "" "") # Install include files - PCL_ADD_INCLUDES("${SUBSYS_NAME}" "${SUBSYS_PATH}" ${dev_incs} ${incs} ${srcs}) + PCL_ADD_INCLUDES("${SUBSYS_NAME}" "${SUBSYS_PATH}" ${incs}) + PCL_ADD_INCLUDES("${SUBSYS_NAME}" "${SUBSYS_PATH}/device" ${dev_incs}) endif() diff --git a/gpu/utils/include/pcl/gpu/utils/device/block.hpp b/gpu/utils/include/pcl/gpu/utils/device/block.hpp index 2ebd5a8a..4740bb20 100644 --- a/gpu/utils/include/pcl/gpu/utils/device/block.hpp +++ b/gpu/utils/include/pcl/gpu/utils/device/block.hpp @@ -96,7 +96,7 @@ namespace pcl } template - static __device__ __forceinline__ void transfrom(InIt beg, InIt end, OutIt out, UnOp op) + static __device__ __forceinline__ void transform(InIt beg, InIt end, OutIt out, UnOp op) { int STRIDE = stride(); InIt t = beg + flattenedThreadId(); @@ -107,7 +107,7 @@ namespace pcl } template - static __device__ __forceinline__ void transfrom(InIt1 beg1, InIt1 end1, InIt2 beg2, OutIt out, BinOp op) + static __device__ __forceinline__ void transform(InIt1 beg1, InIt1 end1, InIt2 beg2, OutIt out, BinOp op) { int STRIDE = stride(); InIt1 t1 = beg1 + flattenedThreadId(); diff --git a/gpu/utils/include/pcl/gpu/utils/device/reduce.hpp b/gpu/utils/include/pcl/gpu/utils/device/reduce.hpp index 3a802b19..2b29b4c4 100644 --- a/gpu/utils/include/pcl/gpu/utils/device/reduce.hpp +++ b/gpu/utils/include/pcl/gpu/utils/device/reduce.hpp @@ -34,8 +34,8 @@ * Author: Anatoly Baskeheev, Itseez Ltd, (myname.mysurname@mycompany.com) */ -#ifndef PCL_GPU_DEVUCE_REDUCE_HPP_ -#define PCL_GPU_DEVUCE_REDUCE_HPP_ +#ifndef PCL_GPU_DEVICE_REDUCE_HPP_ +#define PCL_GPU_DEVICE_REDUCE_HPP_ namespace pcl { diff --git a/io/CMakeLists.txt b/io/CMakeLists.txt index efaa7985..a17cb2df 100644 --- a/io/CMakeLists.txt +++ b/io/CMakeLists.txt @@ -12,6 +12,14 @@ endif(WIN32) PCL_ADD_DOC("${SUBSYS_NAME}") +if(NOT WIN32) + option(PCL_IO_ENABLE_MAND_LOCKING "Enables the use of mandatory locking on filesystems mounted using the mand option." ON) + mark_as_advanced(PCL_IO_ENABLE_MAND_LOCKING) + if (NOT PCL_IO_ENABLE_MAND_LOCKING) + add_definitions(-DNO_MANDATORY_LOCKING) + endif(NOT PCL_IO_ENABLE_MAND_LOCKING) +endif(NOT WIN32) + if(build) if(WITH_OPENNI2) set(IMAGE_INCLUDES @@ -248,6 +256,7 @@ if(build) "include/pcl/${SUBSYS_NAME}/debayer.h" "include/pcl/${SUBSYS_NAME}/file_io.h" "include/pcl/${SUBSYS_NAME}/auto_io.h" + "include/pcl/${SUBSYS_NAME}/low_level_io.h" "include/pcl/${SUBSYS_NAME}/lzf.h" "include/pcl/${SUBSYS_NAME}/lzf_image_io.h" "include/pcl/${SUBSYS_NAME}/io.h" diff --git a/io/include/pcl/compression/color_coding.h b/io/include/pcl/compression/color_coding.h index 382d33a9..8783bcbb 100644 --- a/io/include/pcl/compression/color_coding.h +++ b/io/include/pcl/compression/color_coding.h @@ -294,8 +294,8 @@ namespace pcl /** \brief Decode color information * \param outputCloud_arg output point cloud - * \param beginIdx_arg index indicating first point to be assiged with color information - * \param endIdx_arg index indicating last point to be assiged with color information + * \param beginIdx_arg index indicating first point to be assigned with color information + * \param endIdx_arg index indicating last point to be assigned with color information * \param rgba_offset_arg offset to color information */ void @@ -355,8 +355,8 @@ namespace pcl /** \brief Set default color to points * \param outputCloud_arg output point cloud - * \param beginIdx_arg index indicating first point to be assiged with color information - * \param endIdx_arg index indicating last point to be assiged with color information + * \param beginIdx_arg index indicating first point to be assigned with color information + * \param endIdx_arg index indicating last point to be assigned with color information * \param rgba_offset_arg offset to color information * */ void diff --git a/io/include/pcl/compression/impl/organized_pointcloud_compression.hpp b/io/include/pcl/compression/impl/organized_pointcloud_compression.hpp index 8849aa36..d10f6728 100644 --- a/io/include/pcl/compression/impl/organized_pointcloud_compression.hpp +++ b/io/include/pcl/compression/impl/organized_pointcloud_compression.hpp @@ -88,7 +88,7 @@ namespace pcl compressedDataOut_arg.write (reinterpret_cast (&cloud_height), sizeof (cloud_height)); // encode frame max depth compressedDataOut_arg.write (reinterpret_cast (&maxDepth), sizeof (maxDepth)); - // encode frame focal lenght + // encode frame focal length compressedDataOut_arg.write (reinterpret_cast (&focalLength), sizeof (focalLength)); // encode frame disparity scale compressedDataOut_arg.write (reinterpret_cast (&disparityScale), sizeof (disparityScale)); @@ -186,7 +186,7 @@ namespace pcl compressedDataOut_arg.write (reinterpret_cast (&height_arg), sizeof (height_arg)); // encode frame max depth compressedDataOut_arg.write (reinterpret_cast (&maxDepth), sizeof (maxDepth)); - // encode frame focal lenght + // encode frame focal length compressedDataOut_arg.write (reinterpret_cast (&focalLength_arg), sizeof (focalLength_arg)); // encode frame disparity scale compressedDataOut_arg.write (reinterpret_cast (&disparityScale_arg), sizeof (disparityScale_arg)); diff --git a/io/include/pcl/compression/octree_pointcloud_compression.h b/io/include/pcl/compression/octree_pointcloud_compression.h index de169048..550355ac 100644 --- a/io/include/pcl/compression/octree_pointcloud_compression.h +++ b/io/include/pcl/compression/octree_pointcloud_compression.h @@ -265,13 +265,13 @@ namespace pcl /** \brief Vector for storing binary tree structure */ std::vector binary_tree_data_vector_; - /** \brief Interator on binary tree structure vector */ + /** \brief Iterator on binary tree structure vector */ std::vector binary_color_tree_vector_; /** \brief Vector for storing points per voxel information */ std::vector point_count_data_vector_; - /** \brief Interator on points per voxel vector */ + /** \brief Iterator on points per voxel vector */ std::vector::const_iterator point_count_data_vector_iterator_; /** \brief Color coding instance */ diff --git a/io/include/pcl/compression/organized_pointcloud_compression.h b/io/include/pcl/compression/organized_pointcloud_compression.h index 5485ae31..d75f1327 100644 --- a/io/include/pcl/compression/organized_pointcloud_compression.h +++ b/io/include/pcl/compression/organized_pointcloud_compression.h @@ -122,7 +122,7 @@ namespace pcl * \param[in] compressedDataIn_arg: binary input stream containing compressed data * \param[out] cloud_arg: reference to decoded point cloud * \param[in] bShowStatistics_arg: show compression statistics during decoding - * \return false if an I/O error occured. + * \return false if an I/O error occurred. */ bool decodePointCloud (std::istream& compressedDataIn_arg, PointCloudPtr &cloud_arg, diff --git a/io/include/pcl/compression/organized_pointcloud_conversion.h b/io/include/pcl/compression/organized_pointcloud_conversion.h index d6469274..5e737ea3 100644 --- a/io/include/pcl/compression/organized_pointcloud_conversion.h +++ b/io/include/pcl/compression/organized_pointcloud_conversion.h @@ -324,20 +324,17 @@ namespace pcl if (convertToMono) { // Encode point color - const uint32_t rgb = *reinterpret_cast (&point.rgb); - uint8_t grayvalue = static_cast(0.2989 * static_cast((rgb >> 16) & 0x0000ff) + - 0.5870 * static_cast((rgb >> 8) & 0x0000ff) + - 0.1140 * static_cast((rgb >> 0) & 0x0000ff)); + uint8_t grayvalue = static_cast(0.2989 * point.r + + 0.5870 * point.g + + 0.1140 * point.b); rgbData_arg.push_back (grayvalue); } else { // Encode point color - const uint32_t rgb = *reinterpret_cast (&point.rgb); - - rgbData_arg.push_back ( (rgb >> 16) & 0x0000ff); - rgbData_arg.push_back ( (rgb >> 8) & 0x0000ff); - rgbData_arg.push_back ( (rgb >> 0) & 0x0000ff); + rgbData_arg.push_back (point.r); + rgbData_arg.push_back (point.g); + rgbData_arg.push_back (point.b); } @@ -446,35 +443,22 @@ namespace pcl { if (monoImage_arg) { - const uint8_t& pixel_r = rgbData_arg[i]; - const uint8_t& pixel_g = rgbData_arg[i]; - const uint8_t& pixel_b = rgbData_arg[i]; - // Define point color - uint32_t rgb = (static_cast(pixel_r) << 16 - | static_cast(pixel_g) << 8 - | static_cast(pixel_b)); - newPoint.rgb = *reinterpret_cast(&rgb); + newPoint.r = rgbData_arg[i]; + newPoint.g = rgbData_arg[i]; + newPoint.b = rgbData_arg[i]; } else { - const uint8_t& pixel_r = rgbData_arg[i*3+0]; - const uint8_t& pixel_g = rgbData_arg[i*3+1]; - const uint8_t& pixel_b = rgbData_arg[i*3+2]; - // Define point color - uint32_t rgb = (static_cast(pixel_r) << 16 - | static_cast(pixel_g) << 8 - | static_cast(pixel_b)); - newPoint.rgb = *reinterpret_cast(&rgb); + newPoint.r = rgbData_arg[i*3+0]; + newPoint.g = rgbData_arg[i*3+1]; + newPoint.b = rgbData_arg[i*3+2]; } } else { // Set white point color - uint32_t rgb = (static_cast(255) << 16 - | static_cast(255) << 8 - | static_cast(255)); - newPoint.rgb = *reinterpret_cast(&rgb); + newPoint.rgba = 0xffffffffu; } } else { @@ -564,35 +548,22 @@ namespace pcl { if (monoImage_arg) { - const uint8_t& pixel_r = rgbData_arg[i]; - const uint8_t& pixel_g = rgbData_arg[i]; - const uint8_t& pixel_b = rgbData_arg[i]; - // Define point color - uint32_t rgb = (static_cast(pixel_r) << 16 - | static_cast(pixel_g) << 8 - | static_cast(pixel_b)); - newPoint.rgb = *reinterpret_cast(&rgb); + newPoint.r = rgbData_arg[i]; + newPoint.g = rgbData_arg[i]; + newPoint.b = rgbData_arg[i]; } else { - const uint8_t& pixel_r = rgbData_arg[i*3+0]; - const uint8_t& pixel_g = rgbData_arg[i*3+1]; - const uint8_t& pixel_b = rgbData_arg[i*3+2]; - // Define point color - uint32_t rgb = (static_cast(pixel_r) << 16 - | static_cast(pixel_g) << 8 - | static_cast(pixel_b)); - newPoint.rgb = *reinterpret_cast(&rgb); + newPoint.r = rgbData_arg[i*3+0]; + newPoint.g = rgbData_arg[i*3+1]; + newPoint.b = rgbData_arg[i*3+2]; } } else { // Set white point color - uint32_t rgb = (static_cast(255) << 16 - | static_cast(255) << 8 - | static_cast(255)); - newPoint.rgb = *reinterpret_cast(&rgb); + newPoint.rgba = 0xffffffffu; } } else { diff --git a/io/include/pcl/compression/point_coding.h b/io/include/pcl/compression/point_coding.h index 2c77350a..436370ca 100644 --- a/io/include/pcl/compression/point_coding.h +++ b/io/include/pcl/compression/point_coding.h @@ -162,8 +162,8 @@ namespace pcl /** \brief Decode differential point information * \param outputCloud_arg output point cloud * \param referencePoint_arg coordinates of reference point - * \param beginIdx_arg index indicating first point to be assiged with color information - * \param endIdx_arg index indicating last point to be assiged with color information + * \param beginIdx_arg index indicating first point to be assigned with color information + * \param endIdx_arg index indicating last point to be assigned with color information */ void decodePoints (PointCloudPtr outputCloud_arg, const double* referencePoint_arg, std::size_t beginIdx_arg, diff --git a/io/include/pcl/io/ascii_io.h b/io/include/pcl/io/ascii_io.h index bcd70c77..927b6e53 100644 --- a/io/include/pcl/io/ascii_io.h +++ b/io/include/pcl/io/ascii_io.h @@ -125,7 +125,7 @@ namespace pcl } - /** \brief Set the Separting characters for the ascii point fields 2. + /** \brief Set the Separating characters for the ascii point fields 2. * \param[in] chars string of separating characters * Sets the separating characters for the point fields. The * default separating characters are " \n\t," diff --git a/io/include/pcl/io/boost.h b/io/include/pcl/io/boost.h index 26829bb5..9136d460 100644 --- a/io/include/pcl/io/boost.h +++ b/io/include/pcl/io/boost.h @@ -41,9 +41,9 @@ #if defined __GNUC__ # pragma GCC system_header #endif -#ifndef __CUDACC__ //https://bugreports.qt-project.org/browse/QTBUG-22829 #ifndef Q_MOC_RUN +#ifndef __CUDACC__ #include #include #include @@ -63,17 +63,13 @@ #include #include #include -#include -#ifndef Q_MOC_RUN #include -#endif #if BOOST_VERSION >= 104700 #include #endif #include #include #include -#include #if BOOST_VERSION >= 104900 #include #endif @@ -82,6 +78,8 @@ #include #include #endif +#include +#include #endif #endif // _PCL_IO_BOOST_H_ diff --git a/io/include/pcl/io/file_io.h b/io/include/pcl/io/file_io.h index 02c00ddb..c1c613e2 100644 --- a/io/include/pcl/io/file_io.h +++ b/io/include/pcl/io/file_io.h @@ -49,7 +49,7 @@ namespace pcl { /** \brief Point Cloud Data (FILE) file format reader interface. - * Any (FILE) format file reader should implement its virtual methodes. + * Any (FILE) format file reader should implement its virtual methods. * \author Nizar Sallem * \ingroup io */ @@ -155,7 +155,7 @@ namespace pcl }; /** \brief Point Cloud Data (FILE) file format writer. - * Any (FILE) format file reader should implement its virtual methodes + * Any (FILE) format file reader should implement its virtual methods * \author Nizar Sallem * \ingroup io */ @@ -221,9 +221,9 @@ namespace pcl } }; - /** \brief insers a value of type Type (uchar, char, uint, int, float, double, ...) into a stringstream. + /** \brief inserts a value of type Type (uchar, char, uint, int, float, double, ...) into a stringstream. * - * If the value is NaN, it inserst "nan". + * If the value is NaN, it inserts "nan". * * \param[in] cloud the cloud to copy from * \param[in] point_index the index of the point diff --git a/io/include/pcl/io/fotonic_grabber.h b/io/include/pcl/io/fotonic_grabber.h index 907dc9c4..ef834197 100644 --- a/io/include/pcl/io/fotonic_grabber.h +++ b/io/include/pcl/io/fotonic_grabber.h @@ -135,7 +135,7 @@ namespace pcl void setupDevice (const FZ_DEVICE_INFO& device_info, const Mode& depth_mode, const Mode& image_mode); - /** \brief Continously asks for data from the device and publishes it if available. */ + /** \brief Continuously asks for data from the device and publishes it if available. */ void processGrabbing (); diff --git a/io/include/pcl/io/grabber.h b/io/include/pcl/io/grabber.h index 0858ed48..9c23b26c 100644 --- a/io/include/pcl/io/grabber.h +++ b/io/include/pcl/io/grabber.h @@ -62,7 +62,7 @@ namespace pcl /** \brief Constructor. */ Grabber () : signals_ (), connections_ (), shared_connections_ () {} - /** \brief virtual desctructor. */ + /** \brief virtual destructor. */ virtual inline ~Grabber () throw (); /** \brief registers a callback function/method to a signal with the corresponding signature diff --git a/io/include/pcl/io/hdl_grabber.h b/io/include/pcl/io/hdl_grabber.h index 6817eb58..682628e3 100644 --- a/io/include/pcl/io/hdl_grabber.h +++ b/io/include/pcl/io/hdl_grabber.h @@ -67,13 +67,18 @@ namespace pcl (sig_cb_velodyne_hdl_scan_point_cloud_xyz) (const boost::shared_ptr >&, float, float); + /** \brief Signal used for a single sector * Represents 1 corrected packet from the HDL Velodyne. Each laser has a different RGB */ typedef void - (sig_cb_velodyne_hdl_scan_point_cloud_xyzrgb) (const boost::shared_ptr >&, - float, - float); + (sig_cb_velodyne_hdl_scan_point_cloud_xyzrgba) (const boost::shared_ptr >&, + float, + float); + + typedef PCL_DEPRECATED ("Use 'sig_cb_velodyne_hdl_scan_point_cloud_xyzrgba' instead") + sig_cb_velodyne_hdl_scan_point_cloud_xyzrgba sig_cb_velodyne_hdl_scan_point_cloud_xyzrgb; + /** \brief Signal used for a single sector * Represents 1 corrected packet from the HDL Velodyne with the returned intensity. */ @@ -81,24 +86,30 @@ namespace pcl (sig_cb_velodyne_hdl_scan_point_cloud_xyzi) (const boost::shared_ptr >&, float startAngle, float); + /** \brief Signal used for a 360 degree sweep * Represents multiple corrected packets from the HDL Velodyne * This signal is sent when the Velodyne passes angle "0" */ typedef void (sig_cb_velodyne_hdl_sweep_point_cloud_xyz) (const boost::shared_ptr >&); + /** \brief Signal used for a 360 degree sweep * Represents multiple corrected packets from the HDL Velodyne with the returned intensity * This signal is sent when the Velodyne passes angle "0" */ typedef void (sig_cb_velodyne_hdl_sweep_point_cloud_xyzi) (const boost::shared_ptr >&); + /** \brief Signal used for a 360 degree sweep * Represents multiple corrected packets from the HDL Velodyne * This signal is sent when the Velodyne passes angle "0". Each laser has a different RGB */ typedef void - (sig_cb_velodyne_hdl_sweep_point_cloud_xyzrgb) (const boost::shared_ptr >&); + (sig_cb_velodyne_hdl_sweep_point_cloud_xyzrgba) (const boost::shared_ptr >&); + + typedef PCL_DEPRECATED ("Use 'sig_cb_velodyne_hdl_sweep_point_cloud_xyzrgba' instead") + sig_cb_velodyne_hdl_sweep_point_cloud_xyzrgba sig_cb_velodyne_hdl_sweep_point_cloud_xyzrgb; /** \brief Constructor taking an optional path to an HDL corrections file. The Grabber will listen on the default IP/port for data packets [192.168.3.255/2368] * \param[in] correctionsFile Path to a file which contains the correction parameters for the HDL. This parameter is mandatory for the HDL-64, optional for the HDL-32 @@ -107,13 +118,13 @@ namespace pcl HDLGrabber (const std::string& correctionsFile = "", const std::string& pcapFile = ""); - /** \brief Constructor taking a pecified IP/port and an optional path to an HDL corrections file. + /** \brief Constructor taking a specified IP/port and an optional path to an HDL corrections file. * \param[in] ipAddress IP Address that should be used to listen for HDL packets * \param[in] port UDP Port that should be used to listen for HDL packets * \param[in] correctionsFile Path to a file which contains the correction parameters for the HDL. This field is mandatory for the HDL-64, optional for the HDL-32 */ HDLGrabber (const boost::asio::ip::address& ipAddress, - const unsigned short port, + const uint16_t port, const std::string& correctionsFile = ""); /** \brief virtual Destructor inherited from the Grabber interface. It never throws. */ @@ -150,13 +161,25 @@ namespace pcl */ void filterPackets (const boost::asio::ip::address& ipAddress, - const unsigned short port = 443); + const uint16_t port = 443); - /** \brief Allows one to customize the colors used for each of the lasers. + /** \brief Allows one to customize the colors used by each laser. + * \param[in] color RGB color to set + * \param[in] laserNumber Number of laser to set color */ void setLaserColorRGB (const pcl::RGB& color, - unsigned int laserNumber); + const uint8_t laserNumber); + + /** \brief Allows one to customize the colors used for each of the lasers. + * \param[in] begin begin iterator of RGB color array + * \param[in] end end iterator of RGB color array + */ + template void + setLaserColorRGB (const IterT& begin, const IterT& end) + { + std::copy (begin, end, laser_rgb_mapping_); + } /** \brief Any returns from the HDL with a distance less than this are discarded. * This value is in meters @@ -183,12 +206,17 @@ namespace pcl float getMaximumDistanceThreshold (); + /** \brief Returns the maximum number of lasers + */ + virtual uint8_t + getMaximumNumberOfLasers () const; + protected: - static const int HDL_DATA_PORT = 2368; - static const int HDL_NUM_ROT_ANGLES = 36001; - static const int HDL_LASER_PER_FIRING = 32; - static const int HDL_MAX_NUM_LASERS = 64; - static const int HDL_FIRING_PER_PKT = 12; + static const uint16_t HDL_DATA_PORT = 2368; + static const uint16_t HDL_NUM_ROT_ANGLES = 36001; + static const uint8_t HDL_LASER_PER_FIRING = 32; + static const uint8_t HDL_MAX_NUM_LASERS = 64; + static const uint8_t HDL_FIRING_PER_PKT = 12; enum HDLBlock { @@ -198,24 +226,24 @@ namespace pcl #pragma pack(push, 1) typedef struct HDLLaserReturn { - unsigned short distance; - unsigned char intensity; + uint16_t distance; + uint8_t intensity; } HDLLaserReturn; #pragma pack(pop) struct HDLFiringData { - unsigned short blockIdentifier; - unsigned short rotationalPosition; + uint16_t blockIdentifier; + uint16_t rotationalPosition; HDLLaserReturn laserReturns[HDL_LASER_PER_FIRING]; }; struct HDLDataPacket { HDLFiringData firingData[HDL_FIRING_PER_PKT]; - unsigned int gpsTimestamp; - unsigned char mode; - unsigned char sensorType; + uint32_t gpsTimestamp; + uint8_t mode; + uint8_t sensorType; }; struct HDLLaserCorrection @@ -232,26 +260,26 @@ namespace pcl }; HDLLaserCorrection laser_corrections_[HDL_MAX_NUM_LASERS]; - unsigned int last_azimuth_; + uint16_t last_azimuth_; boost::shared_ptr > current_scan_xyz_, current_sweep_xyz_; boost::shared_ptr > current_scan_xyzi_, current_sweep_xyzi_; - boost::shared_ptr > current_scan_xyzrgb_, current_sweep_xyzrgb_; + boost::shared_ptr > current_scan_xyzrgba_, current_sweep_xyzrgba_; boost::signals2::signal* sweep_xyz_signal_; - boost::signals2::signal* sweep_xyzrgb_signal_; + boost::signals2::signal* sweep_xyzrgba_signal_; boost::signals2::signal* sweep_xyzi_signal_; boost::signals2::signal* scan_xyz_signal_; - boost::signals2::signal* scan_xyzrgb_signal_; + boost::signals2::signal* scan_xyzrgba_signal_; boost::signals2::signal* scan_xyzi_signal_; void fireCurrentSweep (); void - fireCurrentScan (const unsigned short startAngle, - const unsigned short endAngle); + fireCurrentScan (const uint16_t startAngle, + const uint16_t endAngle); void computeXYZI (pcl::PointXYZI& pointXYZI, - int azimuth, + uint16_t azimuth, HDLLaserReturn laserReturn, HDLLaserCorrection correction); @@ -259,10 +287,10 @@ namespace pcl private: static double *cos_lookup_table_; static double *sin_lookup_table_; - pcl::SynchronizedQueue hdl_data_; + pcl::SynchronizedQueue hdl_data_; boost::asio::ip::udp::endpoint udp_listener_endpoint_; boost::asio::ip::address source_address_filter_; - unsigned short source_port_filter_; + uint16_t source_port_filter_; boost::asio::io_service hdl_read_socket_service_; boost::asio::ip::udp::socket *hdl_read_socket_; std::string pcap_file_name_; @@ -286,7 +314,7 @@ namespace pcl processVelodynePackets (); void - enqueueHDLPacket (const unsigned char *data, + enqueueHDLPacket (const uint8_t *data, std::size_t bytesReceived); void diff --git a/io/include/pcl/io/impl/lzf_image_io.hpp b/io/include/pcl/io/impl/lzf_image_io.hpp index 8407d060..d215687a 100644 --- a/io/include/pcl/io/impl/lzf_image_io.hpp +++ b/io/include/pcl/io/impl/lzf_image_io.hpp @@ -146,6 +146,8 @@ pcl::io::LZFDepth16ImageReader::readOMP (const std::string &filename, constant_y = 1.0 / parameters_.focal_length_y; #ifdef _OPENMP #pragma omp parallel for num_threads (num_threads) +#else + (void) num_threads; // suppress warning if OMP is not present #endif for (int i = 0; i < static_cast< int> (cloud.size ()); ++i) { @@ -275,6 +277,8 @@ pcl::io::LZFRGB24ImageReader::readOMP ( #ifdef _OPENMP #pragma omp parallel for num_threads (num_threads) +#else + (void) num_threads; // suppress warning if OMP is not present #endif//_OPENMP for (long int i = 0; i < cloud.size (); ++i) { @@ -385,6 +389,8 @@ pcl::io::LZFYUV422ImageReader::readOMP ( #ifdef _OPENMP #pragma omp parallel for num_threads (num_threads) +#else + (void) num_threads; //suppress warning if OMP is not present #endif//_OPENMP for (int i = 0; i < wh2; ++i) { @@ -496,6 +502,8 @@ pcl::io::LZFBayer8ImageReader::readOMP ( cloud.resize (getWidth () * getHeight ()); #ifdef _OPENMP #pragma omp parallel for num_threads (num_threads) +#else + (void) num_threads; //suppress warning if OMP is not present #endif//_OPENMP for (long int i = 0; i < cloud.size (); ++i) { diff --git a/io/include/pcl/io/impl/pcd_io.hpp b/io/include/pcl/io/impl/pcd_io.hpp index 93799af7..324874c8 100644 --- a/io/include/pcl/io/impl/pcd_io.hpp +++ b/io/include/pcl/io/impl/pcd_io.hpp @@ -44,29 +44,11 @@ #include #include #include -#include #include +#include +#include #include -#ifdef _WIN32 -# include -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif // WIN32_LEAN_AND_MEAN -# ifndef NOMINMAX -# define NOMINMAX -# endif // NOMINMAX -# include -# define pcl_open _open -# define pcl_close(fd) _close(fd) -# define pcl_lseek(fd,offset,origin) _lseek(fd,offset,origin) -#else -# include -# define pcl_open open -# define pcl_close(fd) close(fd) -# define pcl_lseek(fd,offset,origin) lseek(fd,offset,origin) -#endif - #include ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -148,7 +130,7 @@ pcl::PCDWriter::writeBinary (const std::string &file_name, return (-1); } #else - int fd = pcl_open (file_name.c_str (), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + int fd = io::raw_open (file_name.c_str (), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (fd < 0) { throw pcl::IOException ("[pcl::PCDWriter::writeBinary] Error during open!"); @@ -183,36 +165,30 @@ pcl::PCDWriter::writeBinary (const std::string &file_name, // Prepare the map #if _WIN32 HANDLE fm = CreateFileMappingA (h_native_file, NULL, PAGE_READWRITE, 0, (DWORD) (data_idx + data_size), NULL); + if (fm == NULL) + { + throw pcl::IOException("[pcl::PCDWriter::writeBinary] Error during memory map creation ()!"); + return (-1); + } char *map = static_cast(MapViewOfFile (fm, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, data_idx + data_size)); CloseHandle (fm); #else - // Stretch the file size to the size of the data - off_t result = pcl_lseek (fd, getpagesize () + data_size - 1, SEEK_SET); - - if (result < 0) + // Allocate disk space for the entire file to prevent bus errors. + if (io::raw_fallocate (fd, data_idx + data_size) != 0) { - pcl_close (fd); + io::raw_close (fd); resetLockingPermissions (file_name, file_lock); - PCL_ERROR ("[pcl::PCDWriter::writeBinary] lseek errno: %d strerror: %s\n", errno, strerror (errno)); + PCL_ERROR ("[pcl::PCDWriter::writeBinary] posix_fallocate errno: %d strerror: %s\n", errno, strerror (errno)); - throw pcl::IOException ("[pcl::PCDWriter::writeBinary] Error during lseek ()!"); - return (-1); - } - // Write a bogus entry so that the new file size comes in effect - result = static_cast (::write (fd, "", 1)); - if (result != 1) - { - pcl_close (fd); - resetLockingPermissions (file_name, file_lock); - throw pcl::IOException ("[pcl::PCDWriter::writeBinary] Error during write ()!"); + throw pcl::IOException ("[pcl::PCDWriter::writeBinary] Error during posix_fallocate ()!"); return (-1); } - char *map = static_cast (mmap (0, data_idx + data_size, PROT_WRITE, MAP_SHARED, fd, 0)); + char *map = static_cast (::mmap (0, data_idx + data_size, PROT_WRITE, MAP_SHARED, fd, 0)); if (map == reinterpret_cast (-1)) //MAP_FAILED) { - pcl_close (fd); + io::raw_close (fd); resetLockingPermissions (file_name, file_lock); throw pcl::IOException ("[pcl::PCDWriter::writeBinary] Error during mmap ()!"); return (-1); @@ -244,9 +220,9 @@ pcl::PCDWriter::writeBinary (const std::string &file_name, #if _WIN32 UnmapViewOfFile (map); #else - if (munmap (map, (data_idx + data_size)) == -1) + if (::munmap (map, (data_idx + data_size)) == -1) { - pcl_close (fd); + io::raw_close (fd); resetLockingPermissions (file_name, file_lock); throw pcl::IOException ("[pcl::PCDWriter::writeBinary] Error during munmap ()!"); return (-1); @@ -256,7 +232,7 @@ pcl::PCDWriter::writeBinary (const std::string &file_name, #if _WIN32 CloseHandle (h_native_file); #else - pcl_close (fd); + io::raw_close (fd); #endif resetLockingPermissions (file_name, file_lock); return (0); @@ -286,7 +262,7 @@ pcl::PCDWriter::writeBinaryCompressed (const std::string &file_name, return (-1); } #else - int fd = pcl_open (file_name.c_str (), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + int fd = io::raw_open (file_name.c_str (), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (fd < 0) { throw pcl::IOException ("[pcl::PCDWriter::writeBinaryCompressed] Error during open!"); @@ -321,6 +297,15 @@ pcl::PCDWriter::writeBinaryCompressed (const std::string &file_name, // Compute the size of data data_size = cloud.points.size () * fsize; + // If the data is to large the two 32 bit integers used to store the + // compressed and uncompressed size will overflow. + if (data_size * 3 / 2 > std::numeric_limits::max ()) + { + PCL_ERROR ("[pcl::PCDWriter::writeBinaryCompressed] The input data exceeds the maximum size for compressed version 0.7 pcds of %l bytes.\n", + static_cast (std::numeric_limits::max ()) * 2 / 3); + return (-2); + } + ////////////////////////////////////////////////////////////////////// // Empty array holding only the valid data // data_size = nr_points * point_size @@ -337,11 +322,11 @@ pcl::PCDWriter::writeBinaryCompressed (const std::string &file_name, // pters[3] = &only_valid_data[offset_of_plane_RGB]; // std::vector pters (fields.size ()); - int toff = 0; + size_t toff = 0; for (size_t i = 0; i < pters.size (); ++i) { pters[i] = &only_valid_data[toff]; - toff += fields_sizes[i] * static_cast (cloud.points.size ()); + toff += static_cast(fields_sizes[i]) * cloud.points.size(); } // Go over all the points, and copy the data in the appropriate places @@ -374,36 +359,13 @@ pcl::PCDWriter::writeBinaryCompressed (const std::string &file_name, else { #if !_WIN32 - pcl_close (fd); + io::raw_close (fd); #endif resetLockingPermissions (file_name, file_lock); throw pcl::IOException ("[pcl::PCDWriter::writeBinaryCompressed] Error during compression!"); return (-1); } -#if !_WIN32 - // Stretch the file size to the size of the data - off_t result = pcl_lseek (fd, getpagesize () + data_size - 1, SEEK_SET); - if (result < 0) - { - pcl_close (fd); - resetLockingPermissions (file_name, file_lock); - PCL_ERROR ("[pcl::PCDWriter::writeBinary] lseek errno: %d strerror: %s\n", errno, strerror (errno)); - - throw pcl::IOException ("[pcl::PCDWriter::writeBinaryCompressed] Error during lseek ()!"); - return (-1); - } - // Write a bogus entry so that the new file size comes in effect - result = static_cast (::write (fd, "", 1)); - if (result != 1) - { - pcl_close (fd); - resetLockingPermissions (file_name, file_lock); - throw pcl::IOException ("[pcl::PCDWriter::writeBinaryCompressed] Error during write ()!"); - return (-1); - } -#endif - // Prepare the map #if _WIN32 HANDLE fm = CreateFileMapping (h_native_file, NULL, PAGE_READWRITE, 0, compressed_final_size, NULL); @@ -411,10 +373,21 @@ pcl::PCDWriter::writeBinaryCompressed (const std::string &file_name, CloseHandle (fm); #else - char *map = static_cast (mmap (0, compressed_final_size, PROT_WRITE, MAP_SHARED, fd, 0)); + // Allocate disk space for the entire file to prevent bus errors. + if (io::raw_fallocate (fd, compressed_final_size) != 0) + { + io::raw_close (fd); + resetLockingPermissions (file_name, file_lock); + PCL_ERROR ("[pcl::PCDWriter::writeBinaryCompressed] posix_fallocate errno: %d strerror: %s\n", errno, strerror (errno)); + + throw pcl::IOException ("[pcl::PCDWriter::writeBinaryCompressed] Error during posix_fallocate ()!"); + return (-1); + } + + char *map = static_cast (::mmap (0, compressed_final_size, PROT_WRITE, MAP_SHARED, fd, 0)); if (map == reinterpret_cast (-1)) //MAP_FAILED) { - pcl_close (fd); + io::raw_close (fd); resetLockingPermissions (file_name, file_lock); throw pcl::IOException ("[pcl::PCDWriter::writeBinaryCompressed] Error during mmap ()!"); return (-1); @@ -436,9 +409,9 @@ pcl::PCDWriter::writeBinaryCompressed (const std::string &file_name, #if _WIN32 UnmapViewOfFile (map); #else - if (munmap (map, (compressed_final_size)) == -1) + if (::munmap (map, (compressed_final_size)) == -1) { - pcl_close (fd); + io::raw_close (fd); resetLockingPermissions (file_name, file_lock); throw pcl::IOException ("[pcl::PCDWriter::writeBinaryCompressed] Error during munmap ()!"); return (-1); @@ -449,7 +422,7 @@ pcl::PCDWriter::writeBinaryCompressed (const std::string &file_name, #if _WIN32 CloseHandle (h_native_file); #else - pcl_close (fd); + io::raw_close (fd); #endif resetLockingPermissions (file_name, file_lock); @@ -661,7 +634,7 @@ pcl::PCDWriter::writeBinary (const std::string &file_name, return (-1); } #else - int fd = pcl_open (file_name.c_str (), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + int fd = io::raw_open (file_name.c_str (), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (fd < 0) { throw pcl::IOException ("[pcl::PCDWriter::writeBinary] Error during open!"); @@ -700,31 +673,21 @@ pcl::PCDWriter::writeBinary (const std::string &file_name, CloseHandle (fm); #else - // Stretch the file size to the size of the data - off_t result = pcl_lseek (fd, getpagesize () + data_size - 1, SEEK_SET); - if (result < 0) - { - pcl_close (fd); - resetLockingPermissions (file_name, file_lock); - PCL_ERROR ("[pcl::PCDWriter::writeBinary] lseek errno: %d strerror: %s\n", errno, strerror (errno)); - - throw pcl::IOException ("[pcl::PCDWriter::writeBinary] Error during lseek ()!"); - return (-1); - } - // Write a bogus entry so that the new file size comes in effect - result = static_cast (::write (fd, "", 1)); - if (result != 1) + // Allocate disk space for the entire file to prevent bus errors. + if (io::raw_fallocate (fd, data_idx + data_size) != 0) { - pcl_close (fd); + io::raw_close (fd); resetLockingPermissions (file_name, file_lock); - throw pcl::IOException ("[pcl::PCDWriter::writeBinary] Error during write ()!"); + PCL_ERROR ("[pcl::PCDWriter::writeBinary] posix_fallocate errno: %d strerror: %s\n", errno, strerror (errno)); + + throw pcl::IOException ("[pcl::PCDWriter::writeBinary] Error during posix_fallocate ()!"); return (-1); } - char *map = static_cast (mmap (0, data_idx + data_size, PROT_WRITE, MAP_SHARED, fd, 0)); + char *map = static_cast (::mmap (0, data_idx + data_size, PROT_WRITE, MAP_SHARED, fd, 0)); if (map == reinterpret_cast (-1)) //MAP_FAILED) { - pcl_close (fd); + io::raw_close (fd); resetLockingPermissions (file_name, file_lock); throw pcl::IOException ("[pcl::PCDWriter::writeBinary] Error during mmap ()!"); return (-1); @@ -756,9 +719,9 @@ pcl::PCDWriter::writeBinary (const std::string &file_name, #if _WIN32 UnmapViewOfFile (map); #else - if (munmap (map, (data_idx + data_size)) == -1) + if (::munmap (map, (data_idx + data_size)) == -1) { - pcl_close (fd); + io::raw_close (fd); resetLockingPermissions (file_name, file_lock); throw pcl::IOException ("[pcl::PCDWriter::writeBinary] Error during munmap ()!"); return (-1); @@ -768,7 +731,7 @@ pcl::PCDWriter::writeBinary (const std::string &file_name, #if _WIN32 CloseHandle(h_native_file); #else - pcl_close (fd); + io::raw_close (fd); #endif resetLockingPermissions (file_name, file_lock); @@ -949,11 +912,10 @@ pcl::PCDWriter::writeASCII (const std::string &file_name, fs << result << "\n"; } fs.close (); // Close file - + resetLockingPermissions (file_name, file_lock); return (0); } #endif //#ifndef PCL_IO_PCD_IO_H_ - diff --git a/io/include/pcl/io/low_level_io.h b/io/include/pcl/io/low_level_io.h new file mode 100644 index 00000000..c55163a9 --- /dev/null +++ b/io/include/pcl/io/low_level_io.h @@ -0,0 +1,215 @@ +/* + * Software License Agreement (BSD License) + * + * Point Cloud Library (PCL) - www.pointclouds.org + * Copyright (c) 2012-, Open Perception, Inc. + * Copyright (c) 2018 Fizyr BV. - https://fizyr.com + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the copyright holder(s) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * This file defines compatibility wrappers for low level I/O functions. + * Implemented as inlinable functions to prevent any performance overhead. + */ + +#ifndef __PCL_IO_LOW_LEVEL_IO__ +#define __PCL_IO_LOW_LEVEL_IO__ + +#ifdef _WIN32 +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# ifndef NOMINMAX +# define NOMINMAX +# endif +# include +# include +# include +typedef SSIZE_T ssize_t; +#else +# include +# include +# include +# include +# include +# include +#endif + +namespace pcl +{ + namespace io + { +#ifdef _WIN32 + inline int raw_open(const char * pathname, int flags, int mode) + { + return ::_open(pathname, flags, mode); + } + + inline int raw_open(const char * pathname, int flags) + { + return ::_open(pathname, flags); + } + + inline int raw_close(int fd) + { + return ::_close(fd); + } + + inline int raw_lseek(int fd, long offset, int whence) + { + return ::_lseek(fd, offset, whence); + } + + inline int raw_read(int fd, void * buffer, size_t count) + { + return ::_read(fd, buffer, count); + } + + inline int raw_write(int fd, const void * buffer, size_t count) + { + return ::_write(fd, buffer, count); + } + + inline int raw_ftruncate(int fd, long length) + { + return ::_chsize(fd, length); + } + + inline int raw_fallocate(int fd, long length) + { + // Doesn't actually allocate, but best we can do? + return raw_ftruncate(fd, length); + } +#else + inline int raw_open(const char * pathname, int flags, int mode) + { + return ::open(pathname, flags, mode); + } + + inline int raw_open(const char * pathname, int flags) + { + return ::open(pathname, flags); + } + + inline int raw_close(int fd) + { + return ::close(fd); + } + + inline off_t raw_lseek(int fd, off_t offset, int whence) + { + return ::lseek(fd, offset, whence); + } + + inline ssize_t raw_read(int fd, void * buffer, size_t count) + { + return ::read(fd, buffer, count); + } + + inline ssize_t raw_write(int fd, const void * buffer, size_t count) + { + return ::write(fd, buffer, count); + } + + inline int raw_ftruncate(int fd, off_t length) + { + return ::ftruncate(fd, length); + } + +# ifdef __APPLE__ + inline int raw_fallocate(int fd, off_t length) + { + // OS X doesn't have posix_fallocate, but it has a fcntl that does the job. + // It may make the file too big though, so we truncate before returning. + + // Try to allocate contiguous space first. + ::fstore_t store = {F_ALLOCATEALL | F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, length}; + if (::fcntl(fd, F_PREALLOCATE, &store) != -1) + return raw_ftruncate(fd, length); + + // Try fragmented if it failed. + store.fst_flags = F_ALLOCATEALL; + if (::fcntl(fd, F_PREALLOCATE, &store) != -1) + return raw_ftruncate(fd, length); + + // Fragmented also failed. + return -1; + } + +# else // __APPLE__ + inline int raw_fallocate(int fd, off_t length) + { +# ifdef ANDROID + // Android's libc doesn't have posix_fallocate. + if (::fallocate(fd, 0, 0, length) == 0) + return 0; +# else + // Conforming POSIX systems have posix_fallocate. + if (::posix_fallocate(fd, 0, length) == 0) + return 0; +# endif + + // EINVAL should indicate an unsupported filesystem. + // All other errors are passed up. + if (errno != EINVAL) + return -1; + + // Try to deal with unsupported filesystems by simply seeking + writing. + // This may not really allocate space, but the file size will be set. + // Writes to the mmapped file may still trigger SIGBUS errors later. + + // Remember the old position and seek to the desired length. + off_t old_pos = raw_lseek(fd, 0, SEEK_CUR); + if (old_pos == -1) + return -1; + if (raw_lseek(fd, length - 1, SEEK_SET) == -1) + return -1; + + // Write a single byte to resize the file. + char buffer = 0; + ssize_t written = raw_write(fd, &buffer, 1); + + // Seek back to the old position. + if (raw_lseek(fd, old_pos, SEEK_SET) == -1) + return -1; + + // Fail if we didn't write exactly one byte, + if (written != 1) + return -1; + + return 0; + } +# endif // __APPLE +#endif // _WIN32 + + } +} +#endif // __PCL_IO_LOW_LEVEL_IO__ diff --git a/io/include/pcl/io/oni_grabber.h b/io/include/pcl/io/oni_grabber.h index 907a40df..f02831f3 100644 --- a/io/include/pcl/io/oni_grabber.h +++ b/io/include/pcl/io/oni_grabber.h @@ -84,7 +84,7 @@ namespace pcl typedef void (sig_cb_openni_point_cloud_rgba) (const boost::shared_ptr >&); typedef void (sig_cb_openni_point_cloud_i) (const boost::shared_ptr >&); - /** \brief constuctor + /** \brief constructor * \param[in] file_name the path to the ONI file * \param[in] repeat whether the play back should be in an infinite loop or not * \param[in] stream whether the playback should be in streaming mode or in triggered mode. diff --git a/io/include/pcl/io/openni2/openni2_device.h b/io/include/pcl/io/openni2/openni2_device.h index 354de9f5..4ae70f0b 100644 --- a/io/include/pcl/io/openni2/openni2_device.h +++ b/io/include/pcl/io/openni2/openni2_device.h @@ -230,21 +230,21 @@ namespace pcl void setUseDeviceTimer (bool enable); - /** \brief Get absolut number of depth frames in the current stream. + /** \brief Get absolute number of depth frames in the current stream. * This function returns 0 if the current device is not a file stream or * if the current mode has no depth stream. */ int getDepthFrameCount (); - /** \brief Get absolut number of color frames in the current stream. + /** \brief Get absolute number of color frames in the current stream. * This function returns 0 if the current device is not a file stream or * if the current mode has no color stream. */ int getColorFrameCount (); - /** \brief Get absolut number of ir frames in the current stream. + /** \brief Get absolute number of ir frames in the current stream. * This function returns 0 if the current device is not a file stream or * if the current mode has no ir stream. */ diff --git a/io/include/pcl/io/openni2/openni2_device_manager.h b/io/include/pcl/io/openni2/openni2_device_manager.h index 5e6d623a..e4bee570 100644 --- a/io/include/pcl/io/openni2/openni2_device_manager.h +++ b/io/include/pcl/io/openni2/openni2_device_manager.h @@ -59,7 +59,7 @@ namespace pcl OpenNI2DeviceManager (); virtual ~OpenNI2DeviceManager (); - // This may not actually be a sigleton yet. Need to work out cross-dll incerface. + // This may not actually be a singleton yet. Need to work out cross-dll incerface. // Based on http://stackoverflow.com/a/13431981/1789618 static boost::shared_ptr getInstance () { diff --git a/io/include/pcl/io/openni_camera/openni_driver.h b/io/include/pcl/io/openni_camera/openni_driver.h index 797549b6..ca3323f4 100644 --- a/io/include/pcl/io/openni_camera/openni_driver.h +++ b/io/include/pcl/io/openni_camera/openni_driver.h @@ -198,7 +198,7 @@ namespace openni_wrapper /** * @author Suat Gedikli * @brief decomposes the connection string into vendor id and product id. - * @param[in] connection_string the string containing teh connection information + * @param[in] connection_string the string containing the connection information * @param[out] vendorId the vendor id * @param[out] productId the product id */ diff --git a/io/include/pcl/io/pcd_io.h b/io/include/pcl/io/pcd_io.h index 25e54c80..b6a24c2f 100644 --- a/io/include/pcl/io/pcd_io.h +++ b/io/include/pcl/io/pcd_io.h @@ -70,7 +70,7 @@ namespace pcl * - POINTS ... * - DATA ascii/binary * - * Everything that follows \b DATA is intepreted as data points and + * Everything that follows \b DATA is interpreted as data points and * will be read accordingly. * * PCD_V7 represents PCD files with version 0.7 and has an important @@ -84,6 +84,34 @@ namespace pcl PCD_V7 = 1 }; + /** \brief Read a point cloud data header from a PCD-formatted, binary istream. + * + * Load only the meta information (number of points, their types, etc), + * and not the points themselves, from a given PCD stream. Useful for fast + * evaluation of the underlying data structure. + * + * \attention The PCD data is \b always stored in ROW major format! The + * read/write PCD methods will detect column major input and automatically convert it. + * + * \param[in] binary_istream a std::istream with openmode set to std::ios::binary. + * \param[out] cloud the resultant point cloud dataset (only these + * members will be filled: width, height, point_step, + * row_step, fields[]; data is resized but not written) + * \param[out] origin the sensor acquisition origin (only for > PCD_V7 - null if not present) + * \param[out] orientation the sensor acquisition orientation (only for > PCD_V7 - identity if not present) + * \param[out] pcd_version the PCD version of the file (i.e., PCD_V6, PCD_V7) + * \param[out] data_type the type of data (0 = ASCII, 1 = Binary, 2 = Binary compressed) + * \param[out] data_idx the offset of cloud data within the file + * + * \return + * * < 0 (-1) on error + * * == 0 on success + */ + int + readHeader (std::istream &binary_istream, pcl::PCLPointCloud2 &cloud, + Eigen::Vector4f &origin, Eigen::Quaternionf &orientation, int &pcd_version, + int &data_type, unsigned int &data_idx); + /** \brief Read a point cloud data header from a PCD file. * * Load only the meta information (number of points, their types, etc), @@ -94,7 +122,9 @@ namespace pcl * read/write PCD methods will detect column major input and automatically convert it. * * \param[in] file_name the name of the file to load - * \param[out] cloud the resultant point cloud dataset (only the header will be filled) + * \param[out] cloud the resultant point cloud dataset (only these + * members will be filled: width, height, point_step, + * row_step, fields[]; data is resized but not written) * \param[out] origin the sensor acquisition origin (only for > PCD_V7 - null if not present) * \param[out] orientation the sensor acquisition orientation (only for > PCD_V7 - identity if not present) * \param[out] pcd_version the PCD version of the file (i.e., PCD_V6, PCD_V7) @@ -127,7 +157,9 @@ namespace pcl * read/write PCD methods will detect column major input and automatically convert it. * * \param[in] file_name the name of the file to load - * \param[out] cloud the resultant point cloud dataset (only the header will be filled) + * \param[out] cloud the resultant point cloud dataset (only these + * members will be filled: width, height, point_step, + * row_step, fields[]; data is resized but not written) * \param[in] offset the offset of where to expect the PCD Header in the * file (optional parameter). One usage example for setting the offset * parameter is for reading data from a TAR "archive containing multiple @@ -142,6 +174,45 @@ namespace pcl int readHeader (const std::string &file_name, pcl::PCLPointCloud2 &cloud, const int offset = 0); + /** \brief Read the point cloud data (body) from a PCD stream. + * + * Reads the cloud points from a text-formatted stream. For use after + * readHeader(), when the resulting data_type == 0. + * + * \attention This assumes the stream has been seeked to the position + * indicated by the data_idx result of readHeader(). + * + * \param[in] stream the stream from which to read the body. + * \param[out] cloud the resultant point cloud dataset to be filled. + * \param[in] pcd_version the PCD version of the stream (from readHeader()). + * + * \return + * * < 0 (-1) on error + * * == 0 on success + */ + int + readBodyASCII (std::istream &stream, pcl::PCLPointCloud2 &cloud, int pcd_version); + + /** \brief Read the point cloud data (body) from a block of memory. + * + * Reads the cloud points from a binary-formatted memory block. For use + * after readHeader(), when the resulting data_type is nonzero. + * + * \param[in] data the memory location from which to read the body. + * \param[out] cloud the resultant point cloud dataset to be filled. + * \param[in] pcd_version the PCD version of the stream (from readHeader()). + * \param[in] compressed indicates whether the PCD block contains compressed + * data. This should be true if the data_type returne by readHeader() == 2. + * \param[in] data_idx the offset of the body, as reported by readHeader(). + * + * \return + * * < 0 (-1) on error + * * == 0 on success + */ + int + readBodyBinary (const unsigned char *data, pcl::PCLPointCloud2 &cloud, + int pcd_version, bool compressed, unsigned int data_idx); + /** \brief Read a point cloud data from a PCD file and store it into a pcl/PCLPointCloud2. * \param[in] file_name the name of the file containing the actual PointCloud data * \param[out] cloud the resultant PointCloud message read from disk @@ -252,6 +323,23 @@ namespace pcl const Eigen::Quaternionf &orientation); /** \brief Generate the header of a BINARY_COMPRESSED PCD file format + * \param[out] os the stream into which to write the header + * \param[in] cloud the point cloud data message + * \param[in] origin the sensor acquisition origin + * \param[in] orientation the sensor acquisition orientation + * + * \return + * * < 0 (-1) on error + * * == 0 on success + */ + int + generateHeaderBinaryCompressed (std::ostream &os, + const pcl::PCLPointCloud2 &cloud, + const Eigen::Vector4f &origin, + const Eigen::Quaternionf &orientation); + + /** \brief Generate the header of a BINARY_COMPRESSED PCD file format + * \param[out] os the stream into which to write the header * \param[in] cloud the point cloud data message * \param[in] origin the sensor acquisition origin * \param[in] orientation the sensor acquisition orientation @@ -318,12 +406,31 @@ namespace pcl * \param[in] cloud the point cloud data message * \param[in] origin the sensor acquisition origin * \param[in] orientation the sensor acquisition orientation + * \return + * (-1) for a general error + * (-2) if the input cloud is too large for the file format + * 0 on success */ int writeBinaryCompressed (const std::string &file_name, const pcl::PCLPointCloud2 &cloud, const Eigen::Vector4f &origin = Eigen::Vector4f::Zero (), const Eigen::Quaternionf &orientation = Eigen::Quaternionf::Identity ()); + /** \brief Save point cloud data to a std::ostream containing n-D points, in BINARY_COMPRESSED format + * \param[out] os the stream into which to write the data + * \param[in] cloud the point cloud data message + * \param[in] origin the sensor acquisition origin + * \param[in] orientation the sensor acquisition orientation + * \return + * (-1) for a general error + * (-2) if the input cloud is too large for the file format + * 0 on success + */ + int + writeBinaryCompressed (std::ostream &os, const pcl::PCLPointCloud2 &cloud, + const Eigen::Vector4f &origin = Eigen::Vector4f::Zero (), + const Eigen::Quaternionf &orientation = Eigen::Quaternionf::Identity ()); + /** \brief Save point cloud data to a PCD file containing n-D points * \param[in] file_name the output file name * \param[in] cloud the point cloud data message @@ -388,6 +495,10 @@ namespace pcl /** \brief Save point cloud data to a binary comprssed PCD file * \param[in] file_name the output file name * \param[in] cloud the point cloud data message + * \return + * (-1) for a general error + * (-2) if the input cloud is too large for the file format + * 0 on success */ template int writeBinaryCompressed (const std::string &file_name, diff --git a/io/include/pcl/io/ply/byte_order.h b/io/include/pcl/io/ply/byte_order.h index 4fff118f..2af95d01 100644 --- a/io/include/pcl/io/ply/byte_order.h +++ b/io/include/pcl/io/ply/byte_order.h @@ -49,7 +49,7 @@ namespace pcl namespace ply { /** \file byte_order.h - * defines byte shift operations and endianess. + * defines byte shift operations and endianness. * \author Ares Lagae as part of libply, Nizar Sallem * \ingroup io */ diff --git a/io/include/pcl/io/ply/ply_parser.h b/io/include/pcl/io/ply/ply_parser.h index 649d19a4..4405d1a4 100644 --- a/io/include/pcl/io/ply/ply_parser.h +++ b/io/include/pcl/io/ply/ply_parser.h @@ -304,8 +304,7 @@ namespace pcl typedef int flags_type; enum flags { }; - ply_parser (flags_type flags = 0) : - flags_ (flags), + ply_parser () : comment_callback_ (), obj_info_callback_ (), end_header_callback_ (), line_number_ (0), current_element_ () {} @@ -391,8 +390,6 @@ namespace pcl end_element_callback_type end_element_callback; std::vector > properties; }; - - flags_type flags_; info_callback_type info_callback_; warning_callback_type warning_callback_; diff --git a/io/include/pcl/io/ply_io.h b/io/include/pcl/io/ply_io.h index 7f339981..3b525a84 100644 --- a/io/include/pcl/io/ply_io.h +++ b/io/include/pcl/io/ply_io.h @@ -95,6 +95,8 @@ namespace pcl , rgb_offset_before_ (0) , do_resize_ (false) , polygons_ (0) + , r_(0), g_(0), b_(0) + , a_(0), rgba_(0) {} PLYReader (const PLYReader &p) @@ -109,6 +111,8 @@ namespace pcl , rgb_offset_before_ (0) , do_resize_ (false) , polygons_ (0) + , r_(0), g_(0), b_(0) + , a_(0), rgba_(0) { *this = p; } @@ -529,6 +533,12 @@ namespace pcl std::vector *polygons_; public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW + + private: + // RGB values stored by vertexColorCallback() + int32_t r_, g_, b_; + // Color values stored by vertexAlphaCallback() + uint32_t a_, rgba_; }; /** \brief Point Cloud Data (PLY) file format writer. @@ -732,7 +742,7 @@ namespace pcl { /** \brief Load a PLY v.6 file into a templated PointCloud type. * - * Any PLY files containg sensor data will generate a warning as a + * Any PLY files containing sensor data will generate a warning as a * pcl/PCLPointCloud2 message cannot hold the sensor origin. * * \param[in] file_name the name of the file to load @@ -750,7 +760,7 @@ namespace pcl * \param[in] file_name the name of the file to load * \param[in] cloud the resultant templated point cloud * \param[in] origin the sensor acquisition origin (only for > PLY_V7 - null if not present) - * \param[in] orientation the sensor acquisition orientation if availble, + * \param[in] orientation the sensor acquisition orientation if available, * identity if not present * \ingroup io */ @@ -777,7 +787,7 @@ namespace pcl /** \brief Load a PLY file into a PolygonMesh type. * - * Any PLY files containg sensor data will generate a warning as a + * Any PLY files containing sensor data will generate a warning as a * pcl/PolygonMesh message cannot hold the sensor origin. * * \param[in] file_name the name of the file to load diff --git a/io/include/pcl/io/png_io.h b/io/include/pcl/io/png_io.h index a30b140f..20814cac 100644 --- a/io/include/pcl/io/png_io.h +++ b/io/include/pcl/io/png_io.h @@ -100,7 +100,7 @@ namespace pcl PCL_EXPORTS void savePNGFile (const std::string& file_name, const pcl::PointCloud& cloud); - /** \brief Saves a PCLImage (formely ROS sensor_msgs::Image) to PNG file. + /** \brief Saves a PCLImage (formerly ROS sensor_msgs::Image) to PNG file. * \param[in] file_name the name of the file to write to disk * \param[in] image image to save * \ingroup io @@ -109,43 +109,6 @@ namespace pcl PCL_EXPORTS void savePNGFile (const std::string& file_name, const pcl::PCLImage& image); - /** \brief Saves RGB fields of cloud as image to PNG file. - * \param[in] file_name the name of the file to write to disk - * \param[in] cloud point cloud to save - * \ingroup io - */ - template - PCL_DEPRECATED ( - "pcl::io::savePNGFile (file_name, cloud) is deprecated, please use a new generic " - "function pcl::io::savePNGFile (file_name, cloud, field_name) with \"rgb\" as the field name." - ) - void - savePNGFile (const std::string& file_name, const pcl::PointCloud& cloud) - { - std::vector data(cloud.width * cloud.height * 3); - - for (size_t i = 0; i < cloud.points.size (); ++i) - { - data[i*3 + 0] = cloud.points[i].r; - data[i*3 + 1] = cloud.points[i].g; - data[i*3 + 2] = cloud.points[i].b; - } - saveRgbPNGFile(file_name, &data[0], cloud.width, cloud.height); - } - - /** \brief Saves Labeled Point cloud as image to PNG file. - * \param[in] file_name the name of the file to write to disk - * \param[in] cloud point cloud to save - * \ingroup io - * Warning: Converts to 16 bit (for png), labels using more than 16 bits will cause problems - */ - PCL_EXPORTS PCL_DEPRECATED ( - "savePNGFile (file_name, cloud) is deprecated, please use a new generic function " - "savePNGFile (file_name, cloud, field_name) with \"label\" as the field name." - ) - void - savePNGFile (const std::string& file_name, const pcl::PointCloud& cloud); - /** \brief Saves the data from the specified field of the point cloud as image to PNG file. * \param[in] file_name the name of the file to write to disk * \param[in] cloud point cloud to save diff --git a/io/include/pcl/io/real_sense_grabber.h b/io/include/pcl/io/real_sense_grabber.h index 4ddf3f06..583a1ade 100644 --- a/io/include/pcl/io/real_sense_grabber.h +++ b/io/include/pcl/io/real_sense_grabber.h @@ -137,7 +137,7 @@ namespace pcl * If the default is supplied, then the mode closest to VGA at 30 Hz * will be chosen. * \param[in] strict if set to \c true, an exception will be thrown if - * device does not support exactly the mode requsted. Otherwise the + * device does not support exactly the mode requested. Otherwise the * closest available mode is selected. */ RealSenseGrabber (const std::string& device_id = "", const Mode& mode = Mode (), bool strict = false); diff --git a/io/include/pcl/io/vlp_grabber.h b/io/include/pcl/io/vlp_grabber.h index 123f9bcb..64ee63dc 100644 --- a/io/include/pcl/io/vlp_grabber.h +++ b/io/include/pcl/io/vlp_grabber.h @@ -68,7 +68,7 @@ namespace pcl * \param[in] port UDP Port that should be used to listen for VLP packets */ VLPGrabber (const boost::asio::ip::address& ipAddress, - const unsigned short port); + const uint16_t port); /** \brief virtual Destructor inherited from the Grabber interface. It never throws. */ virtual @@ -80,13 +80,45 @@ namespace pcl virtual std::string getName () const; + /** \brief Allows one to customize the colors used by each laser. + * \param[in] color RGB color to set + * \param[in] laserNumber Number of laser to set color + */ + void + setLaserColorRGB (const pcl::RGB& color, + const uint8_t laserNumber); + + /** \brief Allows one to customize the colors used for each of the lasers. + * \param[in] begin begin iterator of RGB color array + * \param[in] end end iterator of RGB color array + */ + template void + setLaserColorRGB (const IterT& begin, const IterT& end) + { + std::copy (begin, end, laser_rgb_mapping_); + } + + /** \brief Returns the maximum number of lasers + */ + virtual uint8_t + getMaximumNumberOfLasers () const; + + protected: + static const uint8_t VLP_MAX_NUM_LASERS = 16; + static const uint8_t VLP_DUAL_MODE = 0x39; + private: + pcl::RGB laser_rgb_mapping_[VLP_MAX_NUM_LASERS]; + virtual void toPointClouds (HDLDataPacket *dataPacket); boost::asio::ip::address getDefaultNetworkAddress (); + void + initializeLaserMapping (); + void loadVLP16Corrections (); diff --git a/io/include/pcl/io/vtk_io.h b/io/include/pcl/io/vtk_io.h index ee02cce4..b965646d 100644 --- a/io/include/pcl/io/vtk_io.h +++ b/io/include/pcl/io/vtk_io.h @@ -45,7 +45,7 @@ #include #include -// Please do not add any functions tha depend on VTK structures to this file! +// Please do not add any functions that depend on VTK structures to this file! // Use vtk_io_lib.h instead. namespace pcl diff --git a/io/src/dinast_grabber.cpp b/io/src/dinast_grabber.cpp index d621bae9..484dc978 100644 --- a/io/src/dinast_grabber.cpp +++ b/io/src/dinast_grabber.cpp @@ -125,8 +125,12 @@ pcl::DinastGrabber::setupDevice (int device_position, const int id_vendor, const sstream << "[pcl::DinastGrabber::setupDevice] libusb initialization failure, LIBUSB_ERROR: "<< ret; PCL_THROW_EXCEPTION (pcl::IOException, sstream.str ()); } - - libusb_set_debug (context_, 3); + + #if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000106) + libusb_set_option (context_, LIBUSB_OPTION_LOG_LEVEL, 3); + #else + libusb_set_debug (context_, 3); + #endif libusb_device **devs = NULL; // Get the list of USB devices @@ -313,7 +317,7 @@ pcl::DinastGrabber::getXYZIPointCloud () { double pixel = image_[x + image_width_ * y]; - // Correcting distortion, data emprically got in a calibration test + // Correcting distortion, data empirically got in a calibration test double xc = static_cast (x - image_width_ / 2); double yc = static_cast (y - image_height_ / 2); double r1 = sqrt (xc * xc + yc * yc); diff --git a/io/src/ensenso_grabber.cpp b/io/src/ensenso_grabber.cpp index a07252ea..55366f32 100644 --- a/io/src/ensenso_grabber.cpp +++ b/io/src/ensenso_grabber.cpp @@ -323,7 +323,7 @@ pcl::EnsensoGrabber::grabSingleCloud (pcl::PointCloud &cloud) cloud.height = height; cloud.is_dense = false; - // Copy data in point cloud (and convert milimeters in meters) + // Copy data in point cloud (and convert millimeters in meters) for (size_t i = 0; i < pointMap.size (); i += 3) { cloud.points[i / 3].x = pointMap[i] / 1000.0; @@ -433,7 +433,7 @@ pcl::EnsensoGrabber::estimateCalibrationPatternPose (Eigen::Affine3d &pattern_po // Convert tf into a matrix if (!jsonTransformationToMatrix (tf.asJson (), pattern_pose)) return (false); - pattern_pose.translation () /= 1000.0; // Convert translation in meters (Ensenso API returns milimeters) + pattern_pose.translation () /= 1000.0; // Convert translation in meters (Ensenso API returns millimeters) return (true); } catch (NxLibException &ex) @@ -461,7 +461,7 @@ pcl::EnsensoGrabber::computeCalibrationMatrix (const std::vector > robot_poses_mm (robot_poses); std::vector robot_poses_json; robot_poses_json.resize (robot_poses.size ()); - for (uint i = 0; i < robot_poses_json.size (); ++i) + for (size_t i = 0; i < robot_poses_json.size (); ++i) { robot_poses_mm[i].translation () *= 1000.0; // Convert meters in millimeters if (!matrixTransformationToJson (robot_poses_mm[i], robot_poses_json[i])) @@ -506,7 +506,7 @@ pcl::EnsensoGrabber::computeCalibrationMatrix (const std::vectorheight = height; cloud->is_dense = false; - // Copy data in point cloud (and convert milimeters in meters) + // Copy data in point cloud (and convert millimeters in meters) for (size_t i = 0; i < pointMap.size (); i += 3) { cloud->points[i / 3].x = pointMap[i] / 1000.0; diff --git a/io/src/hdl_grabber.cpp b/io/src/hdl_grabber.cpp index bc57f165..c1bdb62a 100644 --- a/io/src/hdl_grabber.cpp +++ b/io/src/hdl_grabber.cpp @@ -63,13 +63,13 @@ pcl::HDLGrabber::HDLGrabber (const std::string& correctionsFile, current_sweep_xyz_ (new pcl::PointCloud ()), current_scan_xyzi_ (new pcl::PointCloud ()), current_sweep_xyzi_ (new pcl::PointCloud ()), - current_scan_xyzrgb_ (new pcl::PointCloud ()), - current_sweep_xyzrgb_ (new pcl::PointCloud ()), + current_scan_xyzrgba_ (new pcl::PointCloud ()), + current_sweep_xyzrgba_ (new pcl::PointCloud ()), sweep_xyz_signal_ (), - sweep_xyzrgb_signal_ (), + sweep_xyzrgba_signal_ (), sweep_xyzi_signal_ (), scan_xyz_signal_ (), - scan_xyzrgb_signal_ (), + scan_xyzrgba_signal_ (), scan_xyzi_signal_ (), hdl_data_ (), udp_listener_endpoint_ (), @@ -88,20 +88,20 @@ pcl::HDLGrabber::HDLGrabber (const std::string& correctionsFile, ///////////////////////////////////////////////////////////////////////////// pcl::HDLGrabber::HDLGrabber (const boost::asio::ip::address& ipAddress, - const unsigned short int port, + const uint16_t port, const std::string& correctionsFile) : last_azimuth_ (65000), current_scan_xyz_ (new pcl::PointCloud ()), current_sweep_xyz_ (new pcl::PointCloud ()), current_scan_xyzi_ (new pcl::PointCloud ()), current_sweep_xyzi_ (new pcl::PointCloud ()), - current_scan_xyzrgb_ (new pcl::PointCloud ()), - current_sweep_xyzrgb_ (new pcl::PointCloud ()), + current_scan_xyzrgba_ (new pcl::PointCloud ()), + current_sweep_xyzrgba_ (new pcl::PointCloud ()), sweep_xyz_signal_ (), - sweep_xyzrgb_signal_ (), + sweep_xyzrgba_signal_ (), sweep_xyzi_signal_ (), scan_xyz_signal_ (), - scan_xyzrgb_signal_ (), + scan_xyzrgba_signal_ (), scan_xyzi_signal_ (), hdl_data_ (), udp_listener_endpoint_ (ipAddress, port), @@ -124,10 +124,10 @@ pcl::HDLGrabber::~HDLGrabber () throw () stop (); disconnect_all_slots (); - disconnect_all_slots (); + disconnect_all_slots (); disconnect_all_slots (); disconnect_all_slots (); - disconnect_all_slots (); + disconnect_all_slots (); disconnect_all_slots (); } @@ -139,7 +139,7 @@ pcl::HDLGrabber::initialize (const std::string& correctionsFile) { cos_lookup_table_ = static_cast (malloc (HDL_NUM_ROT_ANGLES * sizeof (*cos_lookup_table_))); sin_lookup_table_ = static_cast (malloc (HDL_NUM_ROT_ANGLES * sizeof (*sin_lookup_table_))); - for (int i = 0; i < HDL_NUM_ROT_ANGLES; i++) + for (uint16_t i = 0; i < HDL_NUM_ROT_ANGLES; i++) { double rad = (M_PI / 180.0) * (static_cast (i) / 100.0); cos_lookup_table_[i] = std::cos (rad); @@ -149,17 +149,17 @@ pcl::HDLGrabber::initialize (const std::string& correctionsFile) loadCorrectionsFile (correctionsFile); - for (int i = 0; i < HDL_MAX_NUM_LASERS; i++) + for (uint8_t i = 0; i < HDL_MAX_NUM_LASERS; i++) { HDLLaserCorrection correction = laser_corrections_[i]; laser_corrections_[i].sinVertOffsetCorrection = correction.verticalOffsetCorrection * correction.sinVertCorrection; laser_corrections_[i].cosVertOffsetCorrection = correction.verticalOffsetCorrection * correction.cosVertCorrection; } sweep_xyz_signal_ = createSignal (); - sweep_xyzrgb_signal_ = createSignal (); + sweep_xyzrgba_signal_ = createSignal (); sweep_xyzi_signal_ = createSignal (); scan_xyz_signal_ = createSignal (); - scan_xyzrgb_signal_ = createSignal (); + scan_xyzrgba_signal_ = createSignal (); scan_xyzi_signal_ = createSignal (); current_scan_xyz_.reset (new pcl::PointCloud); @@ -167,12 +167,12 @@ pcl::HDLGrabber::initialize (const std::string& correctionsFile) current_sweep_xyz_.reset (new pcl::PointCloud); current_sweep_xyzi_.reset (new pcl::PointCloud); - for (int i = 0; i < HDL_MAX_NUM_LASERS; i++) + for (uint8_t i = 0; i < HDL_MAX_NUM_LASERS; i++) laser_rgb_mapping_[i].r = laser_rgb_mapping_[i].g = laser_rgb_mapping_[i].b = 0; if (laser_corrections_[32].distanceCorrection == 0.0) { - for (int i = 0; i < 16; i++) + for (uint8_t i = 0; i < 16; i++) { laser_rgb_mapping_[i * 2].b = static_cast (i * 6 + 64); laser_rgb_mapping_[i * 2 + 1].b = static_cast ( (i + 16) * 6 + 64); @@ -180,12 +180,12 @@ pcl::HDLGrabber::initialize (const std::string& correctionsFile) } else { - for (int i = 0; i < 16; i++) + for (uint8_t i = 0; i < 16; i++) { laser_rgb_mapping_[i * 2].b = static_cast (i * 3 + 64); laser_rgb_mapping_[i * 2 + 1].b = static_cast ( (i + 16) * 3 + 64); } - for (int i = 0; i < 16; i++) + for (uint8_t i = 0; i < 16; i++) { laser_rgb_mapping_[i * 2 + 32].b = static_cast (i * 3 + 160); laser_rgb_mapping_[i * 2 + 33].b = static_cast ( (i + 16) * 3 + 160); @@ -224,7 +224,7 @@ pcl::HDLGrabber::loadCorrectionsFile (const std::string& correctionsFile) if (px.first == "px") { boost::property_tree::ptree calibration_data = px.second; - int index = -1; + int32_t index = -1; double azimuth = 0, vert_correction = 0, dist_correction = 0, vert_offset_correction = 0, horiz_offset_correction = 0; BOOST_FOREACH (boost::property_tree::ptree::value_type &item, calibration_data) @@ -265,7 +265,7 @@ pcl::HDLGrabber::loadHDL32Corrections () { double hdl32_vertical_corrections[] = { -30.67, -9.3299999, -29.33, -8, -28, -6.6700001, -26.67, -5.3299999, -25.33, -4, -24, -2.6700001, -22.67, -1.33, -21.33, 0, -20, 1.33, -18.67, 2.6700001, -17.33, 4, -16, 5.3299999, -14.67, 6.6700001, -13.33, 8, -12, 9.3299999, -10.67, 10.67 }; - for (int i = 0; i < HDL_LASER_PER_FIRING; i++) + for (uint8_t i = 0; i < HDL_LASER_PER_FIRING; i++) { laser_corrections_[i].azimuthCorrection = 0.0; laser_corrections_[i].distanceCorrection = 0.0; @@ -275,7 +275,7 @@ pcl::HDLGrabber::loadHDL32Corrections () laser_corrections_[i].sinVertCorrection = std::sin (HDL_Grabber_toRadians(hdl32_vertical_corrections[i])); laser_corrections_[i].cosVertCorrection = std::cos (HDL_Grabber_toRadians(hdl32_vertical_corrections[i])); } - for (int i = HDL_LASER_PER_FIRING; i < HDL_MAX_NUM_LASERS; i++) + for (uint8_t i = HDL_LASER_PER_FIRING; i < HDL_MAX_NUM_LASERS; i++) { laser_corrections_[i].azimuthCorrection = 0.0; laser_corrections_[i].distanceCorrection = 0.0; @@ -300,7 +300,7 @@ pcl::HDLGrabber::processVelodynePackets () { while (true) { - unsigned char *data; + uint8_t *data; if (!hdl_data_.dequeue (data)) return; @@ -320,7 +320,7 @@ pcl::HDLGrabber::toPointClouds (HDLDataPacket *dataPacket) return; current_scan_xyz_.reset (new pcl::PointCloud ()); - current_scan_xyzrgb_.reset (new pcl::PointCloud ()); + current_scan_xyzrgba_.reset (new pcl::PointCloud ()); current_scan_xyzi_.reset (new pcl::PointCloud ()); time_t system_time; @@ -328,30 +328,30 @@ pcl::HDLGrabber::toPointClouds (HDLDataPacket *dataPacket) time_t velodyne_time = (system_time & 0x00000000ffffffffl) << 32 | dataPacket->gpsTimestamp; current_scan_xyz_->header.stamp = velodyne_time; - current_scan_xyzrgb_->header.stamp = velodyne_time; + current_scan_xyzrgba_->header.stamp = velodyne_time; current_scan_xyzi_->header.stamp = velodyne_time; current_scan_xyz_->header.seq = scan_counter; - current_scan_xyzrgb_->header.seq = scan_counter; + current_scan_xyzrgba_->header.seq = scan_counter; current_scan_xyzi_->header.seq = scan_counter; scan_counter++; - for (int i = 0; i < HDL_FIRING_PER_PKT; ++i) + for (uint8_t i = 0; i < HDL_FIRING_PER_PKT; ++i) { HDLFiringData firing_data = dataPacket->firingData[i]; - int offset = (firing_data.blockIdentifier == BLOCK_0_TO_31) ? 0 : 32; + uint8_t offset = (firing_data.blockIdentifier == BLOCK_0_TO_31) ? 0 : 32; - for (int j = 0; j < HDL_LASER_PER_FIRING; j++) + for (uint8_t j = 0; j < HDL_LASER_PER_FIRING; j++) { if (firing_data.rotationalPosition < last_azimuth_) { - if (current_sweep_xyzrgb_->size () > 0) + if (current_sweep_xyzrgba_->size () > 0) { - current_sweep_xyz_->is_dense = current_sweep_xyzrgb_->is_dense = current_sweep_xyzi_->is_dense = false; + current_sweep_xyz_->is_dense = current_sweep_xyzrgba_->is_dense = current_sweep_xyzi_->is_dense = false; current_sweep_xyz_->header.stamp = velodyne_time; - current_sweep_xyzrgb_->header.stamp = velodyne_time; + current_sweep_xyzrgba_->header.stamp = velodyne_time; current_sweep_xyzi_->header.stamp = velodyne_time; current_sweep_xyz_->header.seq = sweep_counter; - current_sweep_xyzrgb_->header.seq = sweep_counter; + current_sweep_xyzrgba_->header.seq = sweep_counter; current_sweep_xyzi_->header.seq = sweep_counter; sweep_counter++; @@ -359,21 +359,21 @@ pcl::HDLGrabber::toPointClouds (HDLDataPacket *dataPacket) fireCurrentSweep (); } current_sweep_xyz_.reset (new pcl::PointCloud ()); - current_sweep_xyzrgb_.reset (new pcl::PointCloud ()); + current_sweep_xyzrgba_.reset (new pcl::PointCloud ()); current_sweep_xyzi_.reset (new pcl::PointCloud ()); } PointXYZ xyz; PointXYZI xyzi; - PointXYZRGBA xyzrgb; + PointXYZRGBA xyzrgba; computeXYZI (xyzi, firing_data.rotationalPosition, firing_data.laserReturns[j], laser_corrections_[j + offset]); - xyz.x = xyzrgb.x = xyzi.x; - xyz.y = xyzrgb.y = xyzi.y; - xyz.z = xyzrgb.z = xyzi.z; + xyz.x = xyzrgba.x = xyzi.x; + xyz.y = xyzrgba.y = xyzi.y; + xyz.z = xyzrgba.z = xyzi.z; - xyzrgb.rgba = laser_rgb_mapping_[j + offset].rgba; + xyzrgba.rgba = laser_rgb_mapping_[j + offset].rgba; if (pcl_isnan (xyz.x) || pcl_isnan (xyz.y) || pcl_isnan (xyz.z)) { continue; @@ -381,24 +381,24 @@ pcl::HDLGrabber::toPointClouds (HDLDataPacket *dataPacket) current_scan_xyz_->push_back (xyz); current_scan_xyzi_->push_back (xyzi); - current_scan_xyzrgb_->push_back (xyzrgb); + current_scan_xyzrgba_->push_back (xyzrgba); current_sweep_xyz_->push_back (xyz); current_sweep_xyzi_->push_back (xyzi); - current_sweep_xyzrgb_->push_back (xyzrgb); + current_sweep_xyzrgba_->push_back (xyzrgba); last_azimuth_ = firing_data.rotationalPosition; } } - current_scan_xyz_->is_dense = current_scan_xyzrgb_->is_dense = current_scan_xyzi_->is_dense = true; + current_scan_xyz_->is_dense = current_scan_xyzrgba_->is_dense = current_scan_xyzi_->is_dense = true; fireCurrentScan (dataPacket->firingData[0].rotationalPosition, dataPacket->firingData[11].rotationalPosition); } ///////////////////////////////////////////////////////////////////////////// void pcl::HDLGrabber::computeXYZI (pcl::PointXYZI& point, - int azimuth, + uint16_t azimuth, HDLLaserReturn laserReturn, HDLLaserCorrection correction) { @@ -445,8 +445,8 @@ pcl::HDLGrabber::fireCurrentSweep () if (sweep_xyz_signal_ != NULL && sweep_xyz_signal_->num_slots () > 0) sweep_xyz_signal_->operator() (current_sweep_xyz_); - if (sweep_xyzrgb_signal_ != NULL && sweep_xyzrgb_signal_->num_slots () > 0) - sweep_xyzrgb_signal_->operator() (current_sweep_xyzrgb_); + if (sweep_xyzrgba_signal_ != NULL && sweep_xyzrgba_signal_->num_slots () > 0) + sweep_xyzrgba_signal_->operator() (current_sweep_xyzrgba_); if (sweep_xyzi_signal_ != NULL && sweep_xyzi_signal_->num_slots () > 0) sweep_xyzi_signal_->operator() (current_sweep_xyzi_); @@ -454,8 +454,8 @@ pcl::HDLGrabber::fireCurrentSweep () ///////////////////////////////////////////////////////////////////////////// void -pcl::HDLGrabber::fireCurrentScan (const unsigned short startAngle, - const unsigned short endAngle) +pcl::HDLGrabber::fireCurrentScan (const uint16_t startAngle, + const uint16_t endAngle) { const float start = static_cast (startAngle) / 100.0f; const float end = static_cast (endAngle) / 100.0f; @@ -463,8 +463,8 @@ pcl::HDLGrabber::fireCurrentScan (const unsigned short startAngle, if (scan_xyz_signal_->num_slots () > 0) scan_xyz_signal_->operator () (current_scan_xyz_, start, end); - if (scan_xyzrgb_signal_->num_slots () > 0) - scan_xyzrgb_signal_->operator () (current_scan_xyzrgb_, start, end); + if (scan_xyzrgba_signal_->num_slots () > 0) + scan_xyzrgba_signal_->operator () (current_scan_xyzrgba_, start, end); if (scan_xyzi_signal_->num_slots () > 0) scan_xyzi_signal_->operator() (current_scan_xyzi_, start, end); @@ -472,13 +472,13 @@ pcl::HDLGrabber::fireCurrentScan (const unsigned short startAngle, ///////////////////////////////////////////////////////////////////////////// void -pcl::HDLGrabber::enqueueHDLPacket (const unsigned char *data, +pcl::HDLGrabber::enqueueHDLPacket (const uint8_t *data, std::size_t bytesReceived) { if (bytesReceived == 1206) { - unsigned char *dup = static_cast (malloc (bytesReceived * sizeof(unsigned char))); - memcpy (dup, data, bytesReceived * sizeof(unsigned char)); + uint8_t *dup = static_cast (malloc (bytesReceived * sizeof(uint8_t))); + memcpy (dup, data, bytesReceived * sizeof (uint8_t)); hdl_data_.enqueue (dup); } @@ -528,7 +528,7 @@ pcl::HDLGrabber::start () else { #ifdef HAVE_PCAP - hdl_read_packet_thread_ = new boost::thread(boost::bind(&HDLGrabber::readPacketsFromPcap, this)); + hdl_read_packet_thread_ = new boost::thread (boost::bind (&HDLGrabber::readPacketsFromPcap, this)); #endif // #ifdef HAVE_PCAP } } @@ -585,7 +585,7 @@ pcl::HDLGrabber::getFramesPerSecond () const ///////////////////////////////////////////////////////////////////////////// void pcl::HDLGrabber::filterPackets (const boost::asio::ip::address& ipAddress, - const unsigned short port) + const uint16_t port) { source_address_filter_ = ipAddress; source_port_filter_ = port; @@ -594,9 +594,9 @@ pcl::HDLGrabber::filterPackets (const boost::asio::ip::address& ipAddress, ///////////////////////////////////////////////////////////////////////////// void pcl::HDLGrabber::setLaserColorRGB (const pcl::RGB& color, - unsigned int laserNumber) + const uint8_t laserNumber) { - if (laserNumber >= (unsigned int) HDL_MAX_NUM_LASERS) + if (laserNumber >= HDL_MAX_NUM_LASERS) return; laser_rgb_mapping_[laserNumber] = color; @@ -610,7 +610,7 @@ pcl::HDLGrabber::isAddressUnspecified (const boost::asio::ip::address& ipAddress return (ipAddress.is_unspecified ()); #else if (ipAddress.is_v4 ()) - return (ipAddress.to_v4 ().to_ulong() == 0); + return (ipAddress.to_v4 ().to_ulong () == 0); return (false); #endif @@ -644,11 +644,18 @@ pcl::HDLGrabber::getMinimumDistanceThreshold () return (min_distance_threshold_); } +///////////////////////////////////////////////////////////////////////////// +uint8_t +pcl::HDLGrabber::getMaximumNumberOfLasers () const +{ + return (HDL_MAX_NUM_LASERS); +} + ///////////////////////////////////////////////////////////////////////////// void pcl::HDLGrabber::readPacketsFromSocket () { - unsigned char data[1500]; + uint8_t data[1500]; udp::endpoint sender_endpoint; while (!terminate_read_packet_thread_ && hdl_read_socket_->is_open ()) @@ -669,22 +676,22 @@ void pcl::HDLGrabber::readPacketsFromPcap () { struct pcap_pkthdr *header; - const unsigned char *data; - char errbuff[PCAP_ERRBUF_SIZE]; + const uint8_t *data; + int8_t errbuff[PCAP_ERRBUF_SIZE]; - pcap_t *pcap = pcap_open_offline (pcap_file_name_.c_str (), errbuff); + pcap_t *pcap = pcap_open_offline (pcap_file_name_.c_str (), reinterpret_cast (errbuff)); struct bpf_program filter; std::ostringstream string_stream; string_stream << "udp "; - if (!isAddressUnspecified(source_address_filter_)) + if (!isAddressUnspecified (source_address_filter_)) { - string_stream << " and src port " << source_port_filter_ << " and src host " << source_address_filter_.to_string(); + string_stream << " and src port " << source_port_filter_ << " and src host " << source_address_filter_.to_string (); } // PCAP_NETMASK_UNKNOWN should be 0xffffffff, but it's undefined in older PCAP versions - if (pcap_compile (pcap, &filter, string_stream.str ().c_str(), 0, 0xffffffff) == -1) + if (pcap_compile (pcap, &filter, string_stream.str ().c_str (), 0, 0xffffffff) == -1) { PCL_WARN ("[pcl::HDLGrabber::readPacketsFromPcap] Issue compiling filter: %s.\n", pcap_geterr (pcap)); } @@ -694,11 +701,11 @@ pcl::HDLGrabber::readPacketsFromPcap () } struct timeval lasttime; - unsigned long long usec_delay; + uint64_t usec_delay; lasttime.tv_sec = 0; - int returnValue = pcap_next_ex(pcap, &header, &data); + int32_t returnValue = pcap_next_ex (pcap, &header, &data); while (returnValue >= 0 && !terminate_read_packet_thread_) { @@ -715,15 +722,15 @@ pcl::HDLGrabber::readPacketsFromPcap () usec_delay = ((header->ts.tv_sec - lasttime.tv_sec) * 1000000) + (header->ts.tv_usec - lasttime.tv_usec); - boost::this_thread::sleep(boost::posix_time::microseconds(usec_delay)); + boost::this_thread::sleep (boost::posix_time::microseconds (usec_delay)); lasttime.tv_sec = header->ts.tv_sec; lasttime.tv_usec = header->ts.tv_usec; // The ETHERNET header is 42 bytes long; unnecessary - enqueueHDLPacket(data + 42, header->len - 42); + enqueueHDLPacket (data + 42, header->len - 42); - returnValue = pcap_next_ex(pcap, &header, &data); + returnValue = pcap_next_ex (pcap, &header, &data); } } #endif //#ifdef HAVE_PCAP diff --git a/io/src/ifs_io.cpp b/io/src/ifs_io.cpp index 247fca81..6424cbfe 100644 --- a/io/src/ifs_io.cpp +++ b/io/src/ifs_io.cpp @@ -329,7 +329,7 @@ pcl::IFSWriter::write (const std::string &file_name, const pcl::PCLPointCloud2 & if (!cloud.is_dense) { - PCL_ERROR ("[pcl::IFSWriter::write] Non dense cloud are not alowed by IFS format!\n"); + PCL_ERROR ("[pcl::IFSWriter::write] Non dense cloud are not allowed by IFS format!\n"); return (-1); } diff --git a/io/src/lzf.cpp b/io/src/lzf.cpp index 8d52387e..af34210c 100644 --- a/io/src/lzf.cpp +++ b/io/src/lzf.cpp @@ -54,7 +54,11 @@ typedef unsigned int LZF_HSLOT; typedef unsigned int LZF_STATE[1 << (HLOG)]; -#define STRICT_ALIGN !(defined(__i386) || defined (__amd64)) +#if !(defined(__i386) || defined (__amd64)) +# define STRICT_ALIGN 1 +#else +# define STRICT_ALIGN 0 +#endif #if !STRICT_ALIGN /* for unaligned accesses we need a 16 bit datatype. */ # if USHRT_MAX == 65535 diff --git a/io/src/lzf_image_io.cpp b/io/src/lzf_image_io.cpp index 7742f169..06d0c904 100644 --- a/io/src/lzf_image_io.cpp +++ b/io/src/lzf_image_io.cpp @@ -35,6 +35,7 @@ * */ #include +#include #include #include #include @@ -44,19 +45,6 @@ #include #include -#ifdef _WIN32 -# include -# include -# define pcl_open _open -# define pcl_close(fd) _close(fd) -# define pcl_lseek(fd,offset,origin) _lseek(fd,offset,origin) -#else -# include -# define pcl_open open -# define pcl_close(fd) close(fd) -# define pcl_lseek(fd,offset,origin) lseek(fd,offset,origin) -#endif - #define LZF_HEADER_SIZE 37 @@ -87,40 +75,34 @@ pcl::io::LZFImageWriter::saveImageBlob (const char* data, UnmapViewOfFile (map); CloseHandle (h_native_file); #else - int fd = pcl_open (filename.c_str (), O_RDWR | O_CREAT | O_TRUNC, static_cast (0600)); + int fd = raw_open (filename.c_str (), O_RDWR | O_CREAT | O_TRUNC, static_cast (0600)); if (fd < 0) return (false); - // Stretch the file size to the size of the data - off_t result = pcl_lseek (fd, data_size - 1, SEEK_SET); - if (result < 0) - { - pcl_close (fd); - return (false); - } - // Write a bogus entry so that the new file size comes in effect - result = static_cast (::write (fd, "", 1)); - if (result != 1) + + // Allocate disk space for the entire file to prevent bus errors. + if (io::raw_fallocate (fd, data_size) != 0) { - pcl_close (fd); + raw_close (fd); + throw pcl::IOException ("[pcl::PCDWriter::writeBinary] Error during posix_fallocate ()!"); return (false); } - char *map = static_cast (mmap (0, data_size, PROT_WRITE, MAP_SHARED, fd, 0)); + char *map = static_cast (::mmap (0, data_size, PROT_WRITE, MAP_SHARED, fd, 0)); if (map == reinterpret_cast (-1)) // MAP_FAILED { - pcl_close (fd); + raw_close (fd); return (false); } // Copy the data memcpy (&map[0], data, data_size); - if (munmap (map, (data_size)) == -1) + if (::munmap (map, (data_size)) == -1) { - pcl_close (fd); + raw_close (fd); return (false); } - pcl_close (fd); + raw_close (fd); #endif return (true); } @@ -385,7 +367,7 @@ pcl::io::LZFImageReader::loadImageBlob (const std::string &filename, return (false); } // Open for reading - int fd = pcl_open (filename.c_str (), O_RDONLY); + int fd = raw_open (filename.c_str (), O_RDONLY); if (fd == -1) { PCL_ERROR ("[pcl::io::LZFImageReader::loadImage] Failure to open file %s\n", filename.c_str () ); @@ -393,15 +375,15 @@ pcl::io::LZFImageReader::loadImageBlob (const std::string &filename, } // Seek to the end of file to get the filesize - off_t data_size = pcl_lseek (fd, 0, SEEK_END); + long data_size = raw_lseek (fd, 0, SEEK_END); if (data_size < 0) { - pcl_close (fd); + raw_close (fd); PCL_ERROR ("[pcl::io::LZFImageReader::loadImage] lseek errno: %d strerror: %s\n", errno, strerror (errno)); PCL_ERROR ("[pcl::io::LZFImageReader::loadImage] Error during lseek ()!\n"); return (false); } - pcl_lseek (fd, 0, SEEK_SET); + raw_lseek (fd, 0, SEEK_SET); #ifdef _WIN32 // As we don't know the real size of data (compressed or not), @@ -413,15 +395,15 @@ pcl::io::LZFImageReader::loadImageBlob (const std::string &filename, if (map == NULL) { CloseHandle (fm); - pcl_close (fd); + raw_close (fd); PCL_ERROR ("[pcl::io::LZFImageReader::loadImage] Error mapping view of file, %s\n", filename.c_str ()); return (false); } #else - char *map = static_cast (mmap (0, data_size, PROT_READ, MAP_SHARED, fd, 0)); + char *map = static_cast (::mmap (0, data_size, PROT_READ, MAP_SHARED, fd, 0)); if (map == reinterpret_cast (-1)) // MAP_FAILED { - pcl_close (fd); + raw_close (fd); PCL_ERROR ("[pcl::io::LZFImageReader::loadImage] Error preparing mmap for PCLZF file.\n"); return (false); } @@ -437,7 +419,7 @@ pcl::io::LZFImageReader::loadImageBlob (const std::string &filename, UnmapViewOfFile (map); CloseHandle (fm); #else - munmap (map, data_size); + ::munmap (map, data_size); #endif return (false); } @@ -459,7 +441,7 @@ pcl::io::LZFImageReader::loadImageBlob (const std::string &filename, UnmapViewOfFile (map); CloseHandle (fm); #else - munmap (map, data_size); + ::munmap (map, data_size); #endif return (false); } @@ -473,12 +455,12 @@ pcl::io::LZFImageReader::loadImageBlob (const std::string &filename, UnmapViewOfFile (map); CloseHandle (fm); #else - if (munmap (map, data_size) == -1) + if (::munmap (map, data_size) == -1) PCL_ERROR ("[pcl::io::LZFImageReader::loadImage] Munmap failure\n"); #endif - pcl_close (fd); + raw_close (fd); - data_size = off_t (compressed_size); // We only care about this from here on + data_size = compressed_size; // We only care about this from here on return (true); } diff --git a/io/src/obj_io.cpp b/io/src/obj_io.cpp index 3586f689..a2b8ff13 100644 --- a/io/src/obj_io.cpp +++ b/io/src/obj_io.cpp @@ -374,7 +374,6 @@ pcl::OBJReader::readHeader (const std::string &file_name, pcl::PCLPointCloud2 &c // bool material_found = false; std::vector material_files; std::size_t nr_point = 0; - std::vector st; try { @@ -385,40 +384,42 @@ pcl::OBJReader::readHeader (const std::string &file_name, pcl::PCLPointCloud2 &c if (line == "") continue; - // Tokenize the line - std::stringstream sstream (line); - sstream.imbue (std::locale::classic ()); - line = sstream.str (); + // Trim the line boost::trim (line); - boost::split (st, line, boost::is_any_of ("\t\r "), boost::token_compress_on); + // Ignore comments - if (st.at (0) == "#") + if (line[0] == '#') continue; - // Vertex - if (st.at (0) == "v") + // Vertex, vertex texture or vertex normal + if (line[0] == 'v') { - ++nr_point; - continue; - } + // Vertex (v) + if (line[1] == ' ') { + ++nr_point; + continue; + } - // Vertex texture - if ((st.at (0) == "vt") && !vertex_texture_found) - { - vertex_texture_found = true; - continue; - } + // Vertex texture (vt) + else if ((line[1] == 't') && !vertex_texture_found) + { + vertex_texture_found = true; + continue; + } - // Vertex normal - if ((st.at (0) == "vn") && !vertex_normal_found) - { - vertex_normal_found = true; - continue; + // Vertex normal (vn) + else if ((line[1] == 'n') && !vertex_normal_found) + { + vertex_normal_found = true; + continue; + } } // Material library, skip for now! - if (st.at (0) == "mtllib") + if (line.substr (0, 6) == "mtllib") { + std::vector st; + boost::split(st, line, boost::is_any_of("\t\r "), boost::token_compress_on); material_files.push_back (st.at (1)); continue; } @@ -588,6 +589,13 @@ pcl::OBJReader::read (const std::string &file_name, pcl::PCLPointCloud2 &cloud, // Vertex normal if (st[0] == "vn") { + if (normal_idx >= cloud.width) + { + if (normal_idx == cloud.width) + PCL_WARN ("[pcl:OBJReader] Too many vertex normals (expected %d), skipping remaining normals.\n", cloud.width, normal_idx + 1); + ++normal_idx; + continue; + } try { for (int i = 1, f = normal_x_field; i < 4; ++i, ++f) @@ -1129,7 +1137,7 @@ pcl::io::saveOBJFile (const std::string &file_name, // Close obj file fs.close (); - /* Write material defination for OBJ file*/ + /* Write material definition for OBJ file*/ // Open file std::ofstream m_fs; diff --git a/io/src/openni2/openni2_device.cpp b/io/src/openni2/openni2_device.cpp index 3d72828c..6bf8a845 100644 --- a/io/src/openni2/openni2_device.cpp +++ b/io/src/openni2/openni2_device.cpp @@ -93,8 +93,14 @@ pcl::io::openni2::OpenNI2Device::OpenNI2Device (const std::string& device_URI) : { setColorVideoMode (getDefaultColorMode ()); } - setDepthVideoMode (getDefaultDepthMode ()); - setIRVideoMode (getDefaultIRMode ()); + if (openni_device_->hasSensor (openni::SENSOR_DEPTH)) + { + setDepthVideoMode (getDefaultDepthMode ()); + } + if (openni_device_->hasSensor (openni::SENSOR_IR)) + { + setIRVideoMode (getDefaultIRMode ()); + } } if (openni_device_->isFile ()) diff --git a/io/src/openni2_grabber.cpp b/io/src/openni2_grabber.cpp index 1178a363..d7addb67 100644 --- a/io/src/openni2_grabber.cpp +++ b/io/src/openni2_grabber.cpp @@ -347,7 +347,7 @@ pcl::io::OpenNI2Grabber::setupDevice (const std::string& device_id, const Mode& } catch (...) { - PCL_THROW_EXCEPTION (pcl::IOException, "unknown error occured"); + PCL_THROW_EXCEPTION (pcl::IOException, "unknown error occurred"); } typedef pcl::io::openni2::OpenNI2VideoMode VideoMode; diff --git a/io/src/openni_grabber.cpp b/io/src/openni_grabber.cpp index 6f1e6ad7..a67be1b9 100644 --- a/io/src/openni_grabber.cpp +++ b/io/src/openni_grabber.cpp @@ -366,7 +366,7 @@ pcl::OpenNIGrabber::setupDevice (const std::string& device_id, const Mode& depth } catch (...) { - PCL_THROW_EXCEPTION (pcl::IOException, "unknown error occured"); + PCL_THROW_EXCEPTION (pcl::IOException, "unknown error occurred"); } XnMapOutputMode depth_md; diff --git a/io/src/pcd_grabber.cpp b/io/src/pcd_grabber.cpp index 1ee3a727..c7a9ddbb 100644 --- a/io/src/pcd_grabber.cpp +++ b/io/src/pcd_grabber.cpp @@ -36,24 +36,12 @@ */ #include +#include #include #include #include #include #include - -#ifdef _WIN32 -# include -# include -# define pcl_open _open -# define pcl_close(fd) _close(fd) -# define pcl_lseek(fd,offset,origin) _lseek(fd,offset,origin) -#else -# include -# define pcl_open open -# define pcl_close(fd) close(fd) -# define pcl_lseek(fd,offset,origin) lseek(fd,offset,origin) -#endif /////////////////////////////////////////////////////////////////////////////////////////// //////////////////////// GrabberImplementation ////////////////////// @@ -186,7 +174,7 @@ pcl::PCDGrabberBase::PCDGrabberImpl::readAhead () else { tar_offset_ += (tar_header_.getFileSize ()) + (512 - tar_header_.getFileSize () % 512); - int result = static_cast (pcl_lseek (tar_fd_, tar_offset_, SEEK_SET)); + int result = static_cast (io::raw_lseek (tar_fd_, tar_offset_, SEEK_SET)); if (result < 0) closeTARFile (); } @@ -199,7 +187,7 @@ pcl::PCDGrabberBase::PCDGrabberImpl::readAhead () // Try to read in the file as a PCD first valid_ = (reader.read (*pcd_iterator_, next_cloud_, origin_, orientation_, pcd_version) == 0); - // Has an error occured? Check if we can interpret the file as a TAR file first before going onto the next + // Has an error occurred? Check if we can interpret the file as a TAR file first before going onto the next if (!valid_ && openTARFile (*pcd_iterator_) >= 0 && readTARHeader ()) { tar_file_ = *pcd_iterator_; @@ -209,7 +197,7 @@ pcl::PCDGrabberBase::PCDGrabberImpl::readAhead () else { tar_offset_ += (tar_header_.getFileSize ()) + (512 - tar_header_.getFileSize () % 512); - int result = static_cast (pcl_lseek (tar_fd_, tar_offset_, SEEK_SET)); + int result = static_cast (io::raw_lseek (tar_fd_, tar_offset_, SEEK_SET)); if (result < 0) closeTARFile (); } @@ -241,7 +229,7 @@ pcl::PCDGrabberBase::PCDGrabberImpl::readTARHeader () } // We only support regular files for now. - // Addional file types in TAR include: hard links, symbolic links, device/special files, block devices, + // Additional file types in TAR include: hard links, symbolic links, device/special files, block devices, // directories, and named pipes. if (tar_header_.file_type[0] != '0' && tar_header_.file_type[0] != '\0') { @@ -271,7 +259,7 @@ pcl::PCDGrabberBase::PCDGrabberImpl::readTARHeader () void pcl::PCDGrabberBase::PCDGrabberImpl::closeTARFile () { - pcl_close (tar_fd_); + io::raw_close (tar_fd_); tar_fd_ = -1; tar_offset_ = 0; memset (&tar_header_.file_name[0], 0, 512); @@ -281,7 +269,7 @@ pcl::PCDGrabberBase::PCDGrabberImpl::closeTARFile () int pcl::PCDGrabberBase::PCDGrabberImpl::openTARFile (const std::string &file_name) { - tar_fd_ = pcl_open (file_name.c_str (), O_RDONLY); + tar_fd_ = io::raw_open (file_name.c_str (), O_RDONLY); if (tar_fd_ == -1) return (-1); @@ -334,7 +322,7 @@ pcl::PCDGrabberBase::PCDGrabberImpl::scrapeForClouds (bool force) cloud_idx_to_file_idx_.push_back (i); // Update offset tar_offset_ += (tar_header_.getFileSize ()) + (512 - tar_header_.getFileSize () % 512); - int result = static_cast (pcl_lseek (tar_fd_, tar_offset_, SEEK_SET)); + int result = static_cast (io::raw_lseek (tar_fd_, tar_offset_, SEEK_SET)); if (result < 0) break; if (tar_fd_ == -1) diff --git a/io/src/pcd_io.cpp b/io/src/pcd_io.cpp index d8f71e4e..23f73f25 100644 --- a/io/src/pcd_io.cpp +++ b/io/src/pcd_io.cpp @@ -43,25 +43,14 @@ #include #include #include -#include +#include #include +#include #include #include #include -#ifdef _WIN32 -# include -# include -# define pcl_open _open -# define pcl_close(fd) _close(fd) -# define pcl_lseek(fd,offset,origin) _lseek(fd,offset,origin) -#else -# include -# define pcl_open open -# define pcl_close(fd) close(fd) -# define pcl_lseek(fd,offset,origin) lseek(fd,offset,origin) -#endif #include /////////////////////////////////////////////////////////////////////////////////////////// @@ -72,6 +61,7 @@ pcl::PCDWriter::setLockingPermissions (const std::string &file_name, (void)file_name; (void)lock; #ifndef WIN32 +#ifndef NO_MANDATORY_LOCKING // Boost version 1.49 introduced permissions #if BOOST_VERSION >= 104900 // Attempt to lock the file. @@ -93,6 +83,7 @@ pcl::PCDWriter::setLockingPermissions (const std::string &file_name, } #endif #endif +#endif } /////////////////////////////////////////////////////////////////////////////////////////// @@ -103,6 +94,7 @@ pcl::PCDWriter::resetLockingPermissions (const std::string &file_name, (void)file_name; (void)lock; #ifndef WIN32 +#ifndef NO_MANDATORY_LOCKING // Boost version 1.49 introduced permissions #if BOOST_VERSION >= 104900 (void)file_name; @@ -118,13 +110,14 @@ pcl::PCDWriter::resetLockingPermissions (const std::string &file_name, lock.unlock (); #endif #endif +#endif } /////////////////////////////////////////////////////////////////////////////////////////// int -pcl::PCDReader::readHeader (const std::string &file_name, pcl::PCLPointCloud2 &cloud, +pcl::PCDReader::readHeader (std::istream &fs, pcl::PCLPointCloud2 &cloud, Eigen::Vector4f &origin, Eigen::Quaternionf &orientation, - int &pcd_version, int &data_type, unsigned int &data_idx, const int offset) + int &pcd_version, int &data_type, unsigned int &data_idx) { // Default values data_idx = 0; @@ -138,31 +131,11 @@ pcl::PCDReader::readHeader (const std::string &file_name, pcl::PCLPointCloud2 &c // By default, assume that there are _no_ invalid (e.g., NaN) points //cloud.is_dense = true; - int nr_points = 0; - std::ifstream fs; + size_t nr_points = 0; std::string line; int specified_channel_count = 0; - if (file_name == "" || !boost::filesystem::exists (file_name)) - { - PCL_ERROR ("[pcl::PCDReader::readHeader] Could not find file '%s'.\n", file_name.c_str ()); - return (-1); - } - - // Open file in binary mode to avoid problem of - // std::getline() corrupting the result of ifstream::tellg() - fs.open (file_name.c_str (), std::ios::binary); - if (!fs.is_open () || fs.fail ()) - { - PCL_ERROR ("[pcl::PCDReader::readHeader] Could not open file '%s'! Error : %s\n", file_name.c_str (), strerror(errno)); - fs.close (); - return (-1); - } - - // Seek at the given offset - fs.seekg (offset, std::ios::beg); - // field_sizes represents the size of one element in a field (e.g., float = 4, char = 1) // field_counts represents the number of elements in a field (e.g., x = 1, normal_x = 1, fpfh = 33) std::vector field_sizes, field_counts; @@ -293,6 +266,8 @@ pcl::PCDReader::readHeader (const std::string &file_name, pcl::PCLPointCloud2 &c cloud.fields[i].offset = offset; int col_count; sstream >> col_count; + if (col_count < 1) + throw "Invalid COUNT value specified."; cloud.fields[i].count = col_count; offset += col_count * field_sizes[i]; } @@ -305,6 +280,8 @@ pcl::PCDReader::readHeader (const std::string &file_name, pcl::PCLPointCloud2 &c if (line_type.substr (0, 5) == "WIDTH") { sstream >> cloud.width; + if (sstream.fail ()) + throw "Invalid WIDTH value specified."; if (cloud.point_step != 0) cloud.row_step = cloud.point_step * cloud.width; // row_step only makes sense for organized datasets continue; @@ -335,6 +312,8 @@ pcl::PCDReader::readHeader (const std::string &file_name, pcl::PCLPointCloud2 &c // Get the number of points if (line_type.substr (0, 6) == "POINTS") { + if (!cloud.point_step) + throw "Number of POINTS specified before COUNT in header!"; sstream >> nr_points; // Need to allocate: N * point_step cloud.data.resize (nr_points * cloud.point_step); @@ -358,7 +337,6 @@ pcl::PCDReader::readHeader (const std::string &file_name, pcl::PCLPointCloud2 &c catch (const char *exception) { PCL_ERROR ("[pcl::PCDReader::readHeader] %s\n", exception); - fs.close (); return (-1); } @@ -366,7 +344,6 @@ pcl::PCDReader::readHeader (const std::string &file_name, pcl::PCLPointCloud2 &c if (nr_points == 0) { PCL_ERROR ("[pcl::PCDReader::readHeader] No points to read\n"); - fs.close (); return (-1); } @@ -392,41 +369,25 @@ pcl::PCDReader::readHeader (const std::string &file_name, pcl::PCLPointCloud2 &c if (cloud.width == 0 && nr_points != 0) { PCL_ERROR ("[pcl::PCDReader::readHeader] HEIGHT given (%d) but no WIDTH!\n", cloud.height); - fs.close (); return (-1); } } - if (int (cloud.width * cloud.height) != nr_points) + if (cloud.width * cloud.height != nr_points) { PCL_ERROR ("[pcl::PCDReader::readHeader] HEIGHT (%d) x WIDTH (%d) != number of points (%d)\n", cloud.height, cloud.width, nr_points); - fs.close (); return (-1); } - // Close file - fs.close (); - return (0); } /////////////////////////////////////////////////////////////////////////////////////////// int -pcl::PCDReader::readHeader (const std::string &file_name, pcl::PCLPointCloud2 &cloud, const int offset) +pcl::PCDReader::readHeader (const std::string &file_name, pcl::PCLPointCloud2 &cloud, + Eigen::Vector4f &origin, Eigen::Quaternionf &orientation, + int &pcd_version, int &data_type, unsigned int &data_idx, const int offset) { - // Default values - cloud.width = cloud.height = cloud.point_step = cloud.row_step = 0; - cloud.data.clear (); - - // By default, assume that there are _no_ invalid (e.g., NaN) points - //cloud.is_dense = true; - - int nr_points = 0; - std::ifstream fs; - std::string line; - - int specified_channel_count = 0; - if (file_name == "" || !boost::filesystem::exists (file_name)) { PCL_ERROR ("[pcl::PCDReader::readHeader] Could not find file '%s'.\n", file_name.c_str ()); @@ -435,10 +396,11 @@ pcl::PCDReader::readHeader (const std::string &file_name, pcl::PCLPointCloud2 &c // Open file in binary mode to avoid problem of // std::getline() corrupting the result of ifstream::tellg() + std::ifstream fs; fs.open (file_name.c_str (), std::ios::binary); if (!fs.is_open () || fs.fail ()) { - PCL_ERROR ("[pcl::PCDReader::readHeader] Could not open file '%s'! Error : %s\n", file_name.c_str (), strerror(errno)); + PCL_ERROR ("[pcl::PCDReader::readHeader] Could not open file '%s'! Error : %s\n", file_name.c_str (), strerror (errno)); fs.close (); return (-1); } @@ -446,17 +408,45 @@ pcl::PCDReader::readHeader (const std::string &file_name, pcl::PCLPointCloud2 &c // Seek at the given offset fs.seekg (offset, std::ios::beg); - // field_sizes represents the size of one element in a field (e.g., float = 4, char = 1) - // field_counts represents the number of elements in a field (e.g., x = 1, normal_x = 1, fpfh = 33) - std::vector field_sizes, field_counts; - // field_types represents the type of data in a field (e.g., F = float, U = unsigned) - std::vector field_types; + // Delegate parsing to the istream overload. + int result = readHeader (fs, cloud, origin, orientation, pcd_version, data_type, data_idx); + + // Close file + fs.close (); + + return result; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +int +pcl::PCDReader::readHeader (const std::string &file_name, pcl::PCLPointCloud2 &cloud, const int offset) +{ + Eigen::Vector4f origin = Eigen::Vector4f::Zero (); + Eigen::Quaternionf orientation = Eigen::Quaternionf::Identity (); + int pcd_version = 0; + int data_type = 0; + unsigned int data_idx = 0; + + return readHeader (file_name, cloud, origin, orientation, pcd_version, data_type, data_idx, offset); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +int +pcl::PCDReader::readBodyASCII (std::istream &fs, pcl::PCLPointCloud2 &cloud, int /*pcd_version*/) +{ + // Get the number of points the cloud should have + unsigned int nr_points = cloud.width * cloud.height; + + // Setting the is_dense property to true by default + cloud.is_dense = true; + + unsigned int idx = 0; + std::string line; std::vector st; - // Read the header and fill it in with wonderful values try { - while (!fs.eof ()) + while (idx < nr_points && !fs.eof ()) { getline (fs, line); // Ignore empty lines @@ -466,211 +456,238 @@ pcl::PCDReader::readHeader (const std::string &file_name, pcl::PCLPointCloud2 &c // Tokenize the line boost::trim (line); boost::split (st, line, boost::is_any_of ("\t\r "), boost::token_compress_on); - - std::stringstream sstream (line); - sstream.imbue (std::locale::classic ()); - - std::string line_type; - sstream >> line_type; - - // Ignore comments - if (line_type.substr (0, 1) == "#") - continue; - - // Version numbers are not needed for now, but we are checking to see if they're there - if (line_type.substr (0, 7) == "VERSION") - continue; - - // Get the field indices (check for COLUMNS too for backwards compatibility) - if ( (line_type.substr (0, 6) == "FIELDS") || (line_type.substr (0, 7) == "COLUMNS") ) + + if (idx >= nr_points) { - specified_channel_count = static_cast (st.size () - 1); - - // Allocate enough memory to accommodate all fields - cloud.fields.resize (specified_channel_count); - for (int i = 0; i < specified_channel_count; ++i) - { - std::string col_type = st.at (i + 1); - cloud.fields[i].name = col_type; - } - - // Default the sizes and the types of each field to float32 to avoid crashes while using older PCD files - int offset = 0; - for (int i = 0; i < specified_channel_count; ++i, offset += 4) - { - cloud.fields[i].offset = offset; - cloud.fields[i].datatype = pcl::PCLPointField::FLOAT32; - cloud.fields[i].count = 1; - } - cloud.point_step = offset; - continue; + PCL_WARN ("[pcl::PCDReader::read] input has more points (%d) than advertised (%d)!\n", idx, nr_points); + break; } - // Get the field sizes - if (line_type.substr (0, 4) == "SIZE") + size_t total = 0; + // Copy data + for (unsigned int d = 0; d < static_cast (cloud.fields.size ()); ++d) { - specified_channel_count = static_cast (st.size () - 1); - - // Allocate enough memory to accommodate all fields - if (specified_channel_count != static_cast (cloud.fields.size ())) - throw "The number of elements in differs than the number of elements in !"; - - // Resize to accommodate the number of values - field_sizes.resize (specified_channel_count); - - int offset = 0; - for (int i = 0; i < specified_channel_count; ++i) + // Ignore invalid padded dimensions that are inherited from binary data + if (cloud.fields[d].name == "_") { - int col_type ; - sstream >> col_type; - cloud.fields[i].offset = offset; // estimate and save the data offsets - offset += col_type; - field_sizes[i] = col_type; // save a temporary copy - } - cloud.point_step = offset; - //if (cloud.width != 0) - //cloud.row_step = cloud.point_step * cloud.width; - continue; - } - - // Get the field types - if (line_type.substr (0, 4) == "TYPE") - { - if (field_sizes.empty ()) - throw "TYPE of FIELDS specified before SIZE in header!"; - - specified_channel_count = static_cast (st.size () - 1); - - // Allocate enough memory to accommodate all fields - if (specified_channel_count != static_cast (cloud.fields.size ())) - throw "The number of elements in differs than the number of elements in !"; - - // Resize to accommodate the number of values - field_types.resize (specified_channel_count); - - for (int i = 0; i < specified_channel_count; ++i) - { - field_types[i] = st.at (i + 1).c_str ()[0]; - cloud.fields[i].datatype = static_cast (getFieldType (field_sizes[i], field_types[i])); + total += cloud.fields[d].count; // jump over this many elements in the string token + continue; } - continue; - } - - // Get the field counts - if (line_type.substr (0, 5) == "COUNT") - { - if (field_sizes.empty () || field_types.empty ()) - throw "COUNT of FIELDS specified before SIZE or TYPE in header!"; - - specified_channel_count = static_cast (st.size () - 1); - - // Allocate enough memory to accommodate all fields - if (specified_channel_count != static_cast (cloud.fields.size ())) - throw "The number of elements in differs than the number of elements in !"; - - field_counts.resize (specified_channel_count); - - int offset = 0; - for (int i = 0; i < specified_channel_count; ++i) + for (unsigned int c = 0; c < cloud.fields[d].count; ++c) { - cloud.fields[i].offset = offset; - int col_count; - sstream >> col_count; - cloud.fields[i].count = col_count; - offset += col_count * field_sizes[i]; + switch (cloud.fields[d].datatype) + { + case pcl::PCLPointField::INT8: + { + copyStringValue::type> ( + st.at (total + c), cloud, idx, d, c); + break; + } + case pcl::PCLPointField::UINT8: + { + copyStringValue::type> ( + st.at (total + c), cloud, idx, d, c); + break; + } + case pcl::PCLPointField::INT16: + { + copyStringValue::type> ( + st.at (total + c), cloud, idx, d, c); + break; + } + case pcl::PCLPointField::UINT16: + { + copyStringValue::type> ( + st.at (total + c), cloud, idx, d, c); + break; + } + case pcl::PCLPointField::INT32: + { + copyStringValue::type> ( + st.at (total + c), cloud, idx, d, c); + break; + } + case pcl::PCLPointField::UINT32: + { + copyStringValue::type> ( + st.at (total + c), cloud, idx, d, c); + break; + } + case pcl::PCLPointField::FLOAT32: + { + copyStringValue::type> ( + st.at (total + c), cloud, idx, d, c); + break; + } + case pcl::PCLPointField::FLOAT64: + { + copyStringValue::type> ( + st.at (total + c), cloud, idx, d, c); + break; + } + default: + PCL_WARN ("[pcl::PCDReader::read] Incorrect field data type specified (%d)!\n",cloud.fields[d].datatype); + break; + } } - // Adjust the offset for count (number of elements) - cloud.point_step = offset; - continue; - } - - // Get the width of the data (organized point cloud dataset) - if (line_type.substr (0, 5) == "WIDTH") - { - sstream >> cloud.width; - if (cloud.point_step != 0) - cloud.row_step = cloud.point_step * cloud.width; // row_step only makes sense for organized datasets - continue; + total += cloud.fields[d].count; // jump over this many elements in the string token } - - // Get the height of the data (organized point cloud dataset) - if (line_type.substr (0, 6) == "HEIGHT") - { - sstream >> cloud.height; - continue; - } - - // Check the format of the acquisition viewpoint - if (line_type.substr (0, 9) == "VIEWPOINT") - { - if (st.size () < 8) - throw "Not enough number of elements in ! Need 7 values (tx ty tz qw qx qy qz)."; - continue; - } - - // Get the number of points - if (line_type.substr (0, 6) == "POINTS") - { - sstream >> nr_points; - // Need to allocate: N * point_step - cloud.data.resize (nr_points * cloud.point_step); - continue; - } - break; + idx++; } } catch (const char *exception) { - PCL_ERROR ("[pcl::PCDReader::readHeader] %s\n", exception); - fs.close (); + PCL_ERROR ("[pcl::PCDReader::read] %s\n", exception); return (-1); } - // Exit early: if no points have been given, there's no sense to read or check anything anymore - if (nr_points == 0) + if (idx != nr_points) { - PCL_ERROR ("[pcl::PCDReader::readHeader] No points to read\n"); - fs.close (); + PCL_ERROR ("[pcl::PCDReader::read] Number of points read (%d) is different than expected (%d)\n", idx, nr_points); return (-1); } - - // Compatibility with older PCD file versions - if (cloud.width == 0 && cloud.height == 0) - { - cloud.width = nr_points; - cloud.height = 1; - cloud.row_step = cloud.point_step * cloud.width; // row_step only makes sense for organized datasets - } - //assert (cloud.row_step != 0); // If row_step = 0, either point_step was not set or width is 0 - // if both height/width are not given, assume an unorganized dataset - if (cloud.height == 0) + return (0); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +int +pcl::PCDReader::readBodyBinary (const unsigned char *map, pcl::PCLPointCloud2 &cloud, + int /*pcd_version*/, bool compressed, unsigned int data_idx) +{ + // Setting the is_dense property to true by default + cloud.is_dense = true; + + /// ---[ Binary compressed mode only + if (compressed) { - cloud.height = 1; - PCL_WARN ("[pcl::PCDReader::readHeader] no HEIGHT given, setting to 1 (unorganized).\n"); - if (cloud.width == 0) - cloud.width = nr_points; + // Uncompress the data first + unsigned int compressed_size = 0, uncompressed_size = 0; + memcpy (&compressed_size, &map[data_idx + 0], 4); + memcpy (&uncompressed_size, &map[data_idx + 4], 4); + PCL_DEBUG ("[pcl::PCDReader::read] Read a binary compressed file with %u bytes compressed and %u original.\n", compressed_size, uncompressed_size); + + if (uncompressed_size != cloud.data.size ()) + { + PCL_WARN ("[pcl::PCDReader::read] The estimated cloud.data size (%u) is different than the saved uncompressed value (%u)! Data corruption?\n", + cloud.data.size (), uncompressed_size); + cloud.data.resize (uncompressed_size); + } + + unsigned int data_size = static_cast (cloud.data.size ()); + std::vector buf (data_size); + // The size of the uncompressed data better be the same as what we stored in the header + unsigned int tmp_size = pcl::lzfDecompress (&map[data_idx + 8], compressed_size, &buf[0], data_size); + if (tmp_size != uncompressed_size) + { + PCL_ERROR ("[pcl::PCDReader::read] Size of decompressed lzf data (%u) does not match value stored in PCD header (%u). Errno: %d\n", tmp_size, uncompressed_size, errno); + return (-1); + } + + // Get the fields sizes + std::vector fields (cloud.fields.size ()); + std::vector fields_sizes (cloud.fields.size ()); + int nri = 0, fsize = 0; + for (size_t i = 0; i < cloud.fields.size (); ++i) + { + if (cloud.fields[i].name == "_") + continue; + fields_sizes[nri] = cloud.fields[i].count * pcl::getFieldSize (cloud.fields[i].datatype); + fsize += fields_sizes[nri]; + fields[nri] = cloud.fields[i]; + ++nri; + } + fields.resize (nri); + fields_sizes.resize (nri); + + // Unpack the xxyyzz to xyz + std::vector pters (fields.size ()); + int toff = 0; + for (size_t i = 0; i < pters.size (); ++i) + { + pters[i] = &buf[toff]; + toff += fields_sizes[i] * cloud.width * cloud.height; + } + // Copy it to the cloud + for (size_t i = 0; i < cloud.width * cloud.height; ++i) + { + for (size_t j = 0; j < pters.size (); ++j) + { + memcpy (&cloud.data[i * fsize + fields[j].offset], pters[j], fields_sizes[j]); + // Increment the pointer + pters[j] += fields_sizes[j]; + } + } + //memcpy (&cloud.data[0], &buf[0], uncompressed_size); } else + // Copy the data + memcpy (&cloud.data[0], &map[0] + data_idx, cloud.data.size ()); + + // Extra checks (not needed for ASCII) + int point_size = static_cast (cloud.data.size () / (cloud.height * cloud.width)); + // Once copied, we need to go over each field and check if it has NaN/Inf values and assign cloud.is_dense to true or false + for (uint32_t i = 0; i < cloud.width * cloud.height; ++i) { - if (cloud.width == 0 && nr_points != 0) + for (unsigned int d = 0; d < static_cast (cloud.fields.size ()); ++d) { - PCL_ERROR ("[pcl::PCDReader::readHeader] HEIGHT given (%d) but no WIDTH!\n", cloud.height); - fs.close (); - return (-1); + for (uint32_t c = 0; c < cloud.fields[d].count; ++c) + { + switch (cloud.fields[d].datatype) + { + case pcl::PCLPointField::INT8: + { + if (!isValueFinite::type> (cloud, i, point_size, d, c)) + cloud.is_dense = false; + break; + } + case pcl::PCLPointField::UINT8: + { + if (!isValueFinite::type> (cloud, i, point_size, d, c)) + cloud.is_dense = false; + break; + } + case pcl::PCLPointField::INT16: + { + if (!isValueFinite::type> (cloud, i, point_size, d, c)) + cloud.is_dense = false; + break; + } + case pcl::PCLPointField::UINT16: + { + if (!isValueFinite::type> (cloud, i, point_size, d, c)) + cloud.is_dense = false; + break; + } + case pcl::PCLPointField::INT32: + { + if (!isValueFinite::type> (cloud, i, point_size, d, c)) + cloud.is_dense = false; + break; + } + case pcl::PCLPointField::UINT32: + { + if (!isValueFinite::type> (cloud, i, point_size, d, c)) + cloud.is_dense = false; + break; + } + case pcl::PCLPointField::FLOAT32: + { + if (!isValueFinite::type> (cloud, i, point_size, d, c)) + cloud.is_dense = false; + break; + } + case pcl::PCLPointField::FLOAT64: + { + if (!isValueFinite::type> (cloud, i, point_size, d, c)) + cloud.is_dense = false; + break; + } + } + } } } - if (int (cloud.width * cloud.height) != nr_points) - { - PCL_ERROR ("[pcl::PCDReader::readHeader] HEIGHT (%d) x WIDTH (%d) != number of points (%d)\n", cloud.height, cloud.width, nr_points); - fs.close (); - return (-1); - } - - // Close file - fs.close (); - return (0); } @@ -683,6 +700,12 @@ pcl::PCDReader::read (const std::string &file_name, pcl::PCLPointCloud2 &cloud, pcl::console::TicToc tt; tt.tic (); + if (file_name == "" || !boost::filesystem::exists (file_name)) + { + PCL_ERROR ("[pcl::PCDReader::read] Could not find file '%s'.\n", file_name.c_str ()); + return (-1); + } + int data_type; unsigned int data_idx; @@ -690,20 +713,6 @@ pcl::PCDReader::read (const std::string &file_name, pcl::PCLPointCloud2 &cloud, if (res < 0) return (res); - - unsigned int idx = 0; - - // Get the number of points the cloud should have - unsigned int nr_points = cloud.width * cloud.height; - - // Setting the is_dense property to true by default - cloud.is_dense = true; - - if (file_name == "" || !boost::filesystem::exists (file_name)) - { - PCL_ERROR ("[pcl::PCDReader::read] Could not find file '%s'.\n", file_name.c_str ()); - return (-1); - } // if ascii if (data_type == 0) @@ -717,109 +726,10 @@ pcl::PCDReader::read (const std::string &file_name, pcl::PCLPointCloud2 &cloud, return (-1); } - fs.seekg (data_idx); - - std::string line; - std::vector st; + fs.seekg (data_idx + offset); // Read the rest of the file - try - { - while (idx < nr_points && !fs.eof ()) - { - getline (fs, line); - // Ignore empty lines - if (line == "") - continue; - - // Tokenize the line - boost::trim (line); - boost::split (st, line, boost::is_any_of ("\t\r "), boost::token_compress_on); - - if (idx >= nr_points) - { - PCL_WARN ("[pcl::PCDReader::read] input file %s has more points (%d) than advertised (%d)!\n", file_name.c_str (), idx, nr_points); - break; - } - - size_t total = 0; - // Copy data - for (unsigned int d = 0; d < static_cast (cloud.fields.size ()); ++d) - { - // Ignore invalid padded dimensions that are inherited from binary data - if (cloud.fields[d].name == "_") - { - total += cloud.fields[d].count; // jump over this many elements in the string token - continue; - } - for (unsigned int c = 0; c < cloud.fields[d].count; ++c) - { - switch (cloud.fields[d].datatype) - { - case pcl::PCLPointField::INT8: - { - copyStringValue::type> ( - st.at (total + c), cloud, idx, d, c); - break; - } - case pcl::PCLPointField::UINT8: - { - copyStringValue::type> ( - st.at (total + c), cloud, idx, d, c); - break; - } - case pcl::PCLPointField::INT16: - { - copyStringValue::type> ( - st.at (total + c), cloud, idx, d, c); - break; - } - case pcl::PCLPointField::UINT16: - { - copyStringValue::type> ( - st.at (total + c), cloud, idx, d, c); - break; - } - case pcl::PCLPointField::INT32: - { - copyStringValue::type> ( - st.at (total + c), cloud, idx, d, c); - break; - } - case pcl::PCLPointField::UINT32: - { - copyStringValue::type> ( - st.at (total + c), cloud, idx, d, c); - break; - } - case pcl::PCLPointField::FLOAT32: - { - copyStringValue::type> ( - st.at (total + c), cloud, idx, d, c); - break; - } - case pcl::PCLPointField::FLOAT64: - { - copyStringValue::type> ( - st.at (total + c), cloud, idx, d, c); - break; - } - default: - PCL_WARN ("[pcl::PCDReader::read] Incorrect field data type specified (%d)!\n",cloud.fields[d].datatype); - break; - } - } - total += cloud.fields[d].count; // jump over this many elements in the string token - } - idx++; - } - } - catch (const char *exception) - { - PCL_ERROR ("[pcl::PCDReader::read] %s\n", exception); - fs.close (); - return (-1); - } + res = readBodyASCII (fs, cloud, pcd_version); // Close file fs.close (); @@ -829,24 +739,59 @@ pcl::PCDReader::read (const std::string &file_name, pcl::PCLPointCloud2 &cloud, /// We must re-open the file and read with mmap () for binary { // Open for reading - int fd = pcl_open (file_name.c_str (), O_RDONLY); + int fd = io::raw_open (file_name.c_str (), O_RDONLY); if (fd == -1) { PCL_ERROR ("[pcl::PCDReader::read] Failure to open file %s\n", file_name.c_str () ); return (-1); } + + // Infer file size + const size_t file_size = io::raw_lseek (fd, 0, SEEK_END); + io::raw_lseek (fd, 0, SEEK_SET); + + size_t mmap_size = offset + data_idx; // ...because we mmap from the start of the file. + if (data_type == 2) + { + // Seek to real start of data. + long result = io::raw_lseek (fd, offset + data_idx, SEEK_SET); + if (result < 0) + { + io::raw_close (fd); + PCL_ERROR ("[pcl::PCDReader::read] lseek errno: %d strerror: %s\n", errno, strerror (errno)); + PCL_ERROR ("[pcl::PCDReader::read] Error during lseek ()!\n"); + return (-1); + } - // Seek at the given offset - off_t result = pcl_lseek (fd, offset, SEEK_SET); - if (result < 0) + // Read compressed size to compute how much must be mapped + unsigned int compressed_size = 0; + ssize_t num_read = io::raw_read (fd, &compressed_size, 4); + if (num_read < 0) + { + io::raw_close (fd); + PCL_ERROR ("[pcl::PCDReader::read] read errno: %d strerror: %s\n", errno, strerror (errno)); + PCL_ERROR ("[pcl::PCDReader::read] Error during read()!\n"); + return (-1); + } + mmap_size += compressed_size; + // Add the 8 bytes used to store the compressed and uncompressed size + mmap_size += 8; + + // Reset position + io::raw_lseek (fd, 0, SEEK_SET); + } + else + { + mmap_size += cloud.data.size (); + } + + if (mmap_size > file_size) { - pcl_close (fd); - PCL_ERROR ("[pcl::PCDReader::read] lseek errno: %d strerror: %s\n", errno, strerror (errno)); - PCL_ERROR ("[pcl::PCDReader::read] Error during lseek ()!\n"); + io::raw_close (fd); + PCL_ERROR ("[pcl::PCDReader::read] Corrupted PCD file. The file is smaller than expected!\n"); return (-1); } - - size_t data_size = data_idx + cloud.data.size (); + // Prepare the map #ifdef _WIN32 // As we don't know the real size of data (compressed or not), @@ -854,200 +799,45 @@ pcl::PCDReader::read (const std::string &file_name, pcl::PCLPointCloud2 &cloud, HANDLE fm = CreateFileMapping ((HANDLE) _get_osfhandle (fd), NULL, PAGE_READONLY, 0, 0, NULL); // As we don't know the real size of data (compressed or not), // we set dwNumberOfBytesToMap = 0 so as to map the whole file - char *map = static_cast(MapViewOfFile (fm, FILE_MAP_READ, 0, 0, 0)); + unsigned char *map = static_cast (MapViewOfFile (fm, FILE_MAP_READ, 0, 0, 0)); if (map == NULL) { CloseHandle (fm); - pcl_close (fd); + io::raw_close (fd); PCL_ERROR ("[pcl::PCDReader::read] Error mapping view of file, %s\n", file_name.c_str ()); return (-1); } #else - char *map = static_cast (mmap (0, data_size, PROT_READ, MAP_SHARED, fd, 0)); - if (map == reinterpret_cast (-1)) // MAP_FAILED + unsigned char *map = static_cast (::mmap (0, mmap_size, PROT_READ, MAP_SHARED, fd, 0)); + if (map == reinterpret_cast (-1)) // MAP_FAILED { - pcl_close (fd); + io::raw_close (fd); PCL_ERROR ("[pcl::PCDReader::read] Error preparing mmap for binary PCD file.\n"); return (-1); } #endif - /// ---[ Binary compressed mode only - if (data_type == 2) - { - // Uncompress the data first - unsigned int compressed_size, uncompressed_size; - memcpy (&compressed_size, &map[data_idx + 0], sizeof (unsigned int)); - memcpy (&uncompressed_size, &map[data_idx + 4], sizeof (unsigned int)); - PCL_DEBUG ("[pcl::PCDReader::read] Read a binary compressed file with %u bytes compressed and %u original.\n", compressed_size, uncompressed_size); - // For all those weird situations where the compressed data is actually LARGER than the uncompressed one - // (we really ought to check this in the compressor and copy the original data in those cases) - if (data_size < compressed_size || uncompressed_size < compressed_size) - { - PCL_DEBUG ("[pcl::PCDReader::read] Allocated data size (%lu) or uncompressed size (%lu) smaller than compressed size (%u). Need to remap.\n", data_size, uncompressed_size, compressed_size); -#ifdef _WIN32 - UnmapViewOfFile (map); - data_size = compressed_size + data_idx + 8; - map = static_cast(MapViewOfFile (fm, FILE_MAP_READ, 0, 0, data_size)); -#else - munmap (map, data_size); - data_size = compressed_size + data_idx + 8; - map = static_cast (mmap (0, data_size, PROT_READ, MAP_SHARED, fd, 0)); -#endif - } - - if (uncompressed_size != cloud.data.size ()) - { - PCL_WARN ("[pcl::PCDReader::read] The estimated cloud.data size (%u) is different than the saved uncompressed value (%u)! Data corruption?\n", - cloud.data.size (), uncompressed_size); - cloud.data.resize (uncompressed_size); - } - - char *buf = static_cast (malloc (data_size)); - // The size of the uncompressed data better be the same as what we stored in the header - unsigned int tmp_size = pcl::lzfDecompress (&map[data_idx + 8], compressed_size, buf, static_cast (data_size)); - if (tmp_size != uncompressed_size) - { - free (buf); - pcl_close (fd); - PCL_ERROR ("[pcl::PCDReader::read] Size of decompressed lzf data (%u) does not match value stored in PCD header (%u). Errno: %d\n", tmp_size, uncompressed_size, errno); - return (-1); - } - - // Get the fields sizes - std::vector fields (cloud.fields.size ()); - std::vector fields_sizes (cloud.fields.size ()); - int nri = 0, fsize = 0; - for (size_t i = 0; i < cloud.fields.size (); ++i) - { - if (cloud.fields[i].name == "_") - continue; - fields_sizes[nri] = cloud.fields[i].count * pcl::getFieldSize (cloud.fields[i].datatype); - fsize += fields_sizes[nri]; - fields[nri] = cloud.fields[i]; - ++nri; - } - fields.resize (nri); - fields_sizes.resize (nri); - - // Unpack the xxyyzz to xyz - std::vector pters (fields.size ()); - int toff = 0; - for (size_t i = 0; i < pters.size (); ++i) - { - pters[i] = &buf[toff]; - toff += fields_sizes[i] * cloud.width * cloud.height; - } - // Copy it to the cloud - for (size_t i = 0; i < cloud.width * cloud.height; ++i) - { - for (size_t j = 0; j < pters.size (); ++j) - { - memcpy (&cloud.data[i * fsize + fields[j].offset], pters[j], fields_sizes[j]); - // Increment the pointer - pters[j] += fields_sizes[j]; - } - } - //memcpy (&cloud.data[0], &buf[0], uncompressed_size); - - free (buf); - } - else - // Copy the data - memcpy (&cloud.data[0], &map[0] + data_idx, cloud.data.size ()); + res = readBodyBinary (map, cloud, pcd_version, data_type == 2, offset + data_idx); // Unmap the pages of memory #ifdef _WIN32 UnmapViewOfFile (map); CloseHandle (fm); #else - if (munmap (map, data_size) == -1) + if (::munmap (map, mmap_size) == -1) { - pcl_close (fd); + io::raw_close (fd); PCL_ERROR ("[pcl::PCDReader::read] Munmap failure\n"); return (-1); } #endif - pcl_close (fd); - } - - if ((idx != nr_points) && (data_type == 0)) - { - PCL_ERROR ("[pcl::PCDReader::read] Number of points read (%d) is different than expected (%d)\n", idx, nr_points); - return (-1); - } - - // No need to do any extra checks if the data type is ASCII - if (data_type != 0) - { - int point_size = static_cast (cloud.data.size () / (cloud.height * cloud.width)); - // Once copied, we need to go over each field and check if it has NaN/Inf values and assign cloud.is_dense to true or false - for (uint32_t i = 0; i < cloud.width * cloud.height; ++i) - { - for (unsigned int d = 0; d < static_cast (cloud.fields.size ()); ++d) - { - for (uint32_t c = 0; c < cloud.fields[d].count; ++c) - { - switch (cloud.fields[d].datatype) - { - case pcl::PCLPointField::INT8: - { - if (!isValueFinite::type>(cloud, i, point_size, d, c)) - cloud.is_dense = false; - break; - } - case pcl::PCLPointField::UINT8: - { - if (!isValueFinite::type>(cloud, i, point_size, d, c)) - cloud.is_dense = false; - break; - } - case pcl::PCLPointField::INT16: - { - if (!isValueFinite::type>(cloud, i, point_size, d, c)) - cloud.is_dense = false; - break; - } - case pcl::PCLPointField::UINT16: - { - if (!isValueFinite::type>(cloud, i, point_size, d, c)) - cloud.is_dense = false; - break; - } - case pcl::PCLPointField::INT32: - { - if (!isValueFinite::type>(cloud, i, point_size, d, c)) - cloud.is_dense = false; - break; - } - case pcl::PCLPointField::UINT32: - { - if (!isValueFinite::type>(cloud, i, point_size, d, c)) - cloud.is_dense = false; - break; - } - case pcl::PCLPointField::FLOAT32: - { - if (!isValueFinite::type>(cloud, i, point_size, d, c)) - cloud.is_dense = false; - break; - } - case pcl::PCLPointField::FLOAT64: - { - if (!isValueFinite::type>(cloud, i, point_size, d, c)) - cloud.is_dense = false; - break; - } - } - } - } - } + io::raw_close (fd); } double total_time = tt.toc (); PCL_DEBUG ("[pcl::PCDReader::read] Loaded %s as a %s cloud in %g ms with %d points. Available dimensions: %s.\n", file_name.c_str (), cloud.is_dense ? "dense" : "non-dense", total_time, cloud.width * cloud.height, pcl::getFieldsList (cloud).c_str ()); - return (0); + return res; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1258,15 +1048,15 @@ pcl::PCDWriter::generateHeaderBinary (const pcl::PCLPointCloud2 &cloud, } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -std::string -pcl::PCDWriter::generateHeaderBinaryCompressed (const pcl::PCLPointCloud2 &cloud, +int +pcl::PCDWriter::generateHeaderBinaryCompressed (std::ostream &os, + const pcl::PCLPointCloud2 &cloud, const Eigen::Vector4f &origin, const Eigen::Quaternionf &orientation) { - std::ostringstream oss; - oss.imbue (std::locale::classic ()); + os.imbue (std::locale::classic ()); - oss << "# .PCD v0.7 - Point Cloud Data file format" + os << "# .PCD v0.7 - Point Cloud Data file format" "\nVERSION 0.7" "\nFIELDS"; @@ -1279,7 +1069,7 @@ pcl::PCDWriter::generateHeaderBinaryCompressed (const pcl::PCLPointCloud2 &cloud if (fsize > cloud.point_step) { PCL_ERROR ("[pcl::PCDWriter::generateHeaderBinaryCompressed] The size of the fields (%d) is larger than point_step (%d)! Something is wrong here...\n", fsize, cloud.point_step); - return (""); + return (-1); } std::stringstream field_names, field_types, field_sizes, field_counts; @@ -1296,18 +1086,29 @@ pcl::PCDWriter::generateHeaderBinaryCompressed (const pcl::PCLPointCloud2 &cloud if (count == 0) count = 1; // check for 0 counts (coming from older converter code) field_counts << " " << count; } - oss << field_names.str (); - oss << "\nSIZE" << field_sizes.str () + os << field_names.str (); + os << "\nSIZE" << field_sizes.str () << "\nTYPE" << field_types.str () << "\nCOUNT" << field_counts.str (); - oss << "\nWIDTH " << cloud.width << "\nHEIGHT " << cloud.height << "\n"; + os << "\nWIDTH " << cloud.width << "\nHEIGHT " << cloud.height << "\n"; - oss << "VIEWPOINT " << origin[0] << " " << origin[1] << " " << origin[2] << " " << orientation.w () << " " << + os << "VIEWPOINT " << origin[0] << " " << origin[1] << " " << origin[2] << " " << orientation.w () << " " << orientation.x () << " " << orientation.y () << " " << orientation.z () << "\n"; - oss << "POINTS " << cloud.width * cloud.height << "\n"; + os << "POINTS " << cloud.width * cloud.height << "\n"; - return (oss.str ()); + return (os ? 0 : -1); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +std::string +pcl::PCDWriter::generateHeaderBinaryCompressed (const pcl::PCLPointCloud2 &cloud, + const Eigen::Vector4f &origin, + const Eigen::Quaternionf &orientation) +{ + std::ostringstream oss; + generateHeaderBinaryCompressed (oss, cloud, origin, orientation); + return oss.str (); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1460,7 +1261,7 @@ pcl::PCDWriter::writeBinary (const std::string &file_name, const pcl::PCLPointCl setLockingPermissions (file_name, file_lock); #else - int fd = pcl_open (file_name.c_str (), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + int fd = io::raw_open (file_name.c_str (), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (fd < 0) { PCL_ERROR ("[pcl::PCDWriter::writeBinary] Error during open (%s)!\n", file_name.c_str()); @@ -1471,10 +1272,10 @@ pcl::PCDWriter::writeBinary (const std::string &file_name, const pcl::PCLPointCl setLockingPermissions (file_name, file_lock); // Stretch the file size to the size of the data - off_t result = pcl_lseek (fd, getpagesize () + cloud.data.size () - 1, SEEK_SET); + long result = io::raw_lseek (fd, getpagesize () + cloud.data.size () - 1, SEEK_SET); if (result < 0) { - pcl_close (fd); + io::raw_close (fd); resetLockingPermissions (file_name, file_lock); PCL_ERROR ("[pcl::PCDWriter::writeBinary] lseek errno: %d strerror: %s\n", errno, strerror (errno)); PCL_ERROR ("[pcl::PCDWriter::writeBinary] Error during lseek ()!\n"); @@ -1484,7 +1285,7 @@ pcl::PCDWriter::writeBinary (const std::string &file_name, const pcl::PCLPointCl result = static_cast (::write (fd, "", 1)); if (result != 1) { - pcl_close (fd); + io::raw_close (fd); resetLockingPermissions (file_name, file_lock); PCL_ERROR ("[pcl::PCDWriter::writeBinary] Error during write ()!\n"); return (-1); @@ -1500,7 +1301,7 @@ pcl::PCDWriter::writeBinary (const std::string &file_name, const pcl::PCLPointCl char *map = static_cast (mmap (0, static_cast (data_idx + cloud.data.size ()), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); if (map == reinterpret_cast (-1)) // MAP_FAILED { - pcl_close (fd); + io::raw_close (fd); resetLockingPermissions (file_name, file_lock); PCL_ERROR ("[pcl::PCDWriter::writeBinary] Error during mmap ()!\n"); return (-1); @@ -1523,9 +1324,9 @@ pcl::PCDWriter::writeBinary (const std::string &file_name, const pcl::PCLPointCl #ifdef _WIN32 UnmapViewOfFile (map); #else - if (munmap (map, static_cast (data_idx + cloud.data.size ())) == -1) + if (::munmap (map, static_cast (data_idx + cloud.data.size ())) == -1) { - pcl_close (fd); + io::raw_close (fd); resetLockingPermissions (file_name, file_lock); PCL_ERROR ("[pcl::PCDWriter::writeBinary] Error during munmap ()!\n"); return (-1); @@ -1535,7 +1336,7 @@ pcl::PCDWriter::writeBinary (const std::string &file_name, const pcl::PCLPointCl #ifdef _WIN32 CloseHandle(h_native_file); #else - pcl_close (fd); + io::raw_close (fd); #endif resetLockingPermissions (file_name, file_lock); return (0); @@ -1543,7 +1344,7 @@ pcl::PCDWriter::writeBinary (const std::string &file_name, const pcl::PCLPointCl /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int -pcl::PCDWriter::writeBinaryCompressed (const std::string &file_name, const pcl::PCLPointCloud2 &cloud, +pcl::PCDWriter::writeBinaryCompressed (std::ostream &os, const pcl::PCLPointCloud2 &cloud, const Eigen::Vector4f &origin, const Eigen::Quaternionf &orientation) { if (cloud.data.empty ()) @@ -1551,32 +1352,11 @@ pcl::PCDWriter::writeBinaryCompressed (const std::string &file_name, const pcl:: PCL_ERROR ("[pcl::PCDWriter::writeBinaryCompressed] Input point cloud has no data!\n"); return (-1); } - std::streamoff data_idx = 0; - std::ostringstream oss; - oss.imbue (std::locale::classic ()); - oss << generateHeaderBinaryCompressed (cloud, origin, orientation) << "DATA binary_compressed\n"; - oss.flush (); - data_idx = oss.tellp (); - -#ifdef _WIN32 - HANDLE h_native_file = CreateFile (file_name.c_str (), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (h_native_file == INVALID_HANDLE_VALUE) - { - PCL_ERROR ("[pcl::PCDWriter::writeBinaryCompressed] Error during CreateFile (%s)!\n", file_name.c_str ()); - return (-1); - } -#else - int fd = pcl_open (file_name.c_str (), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - if (fd < 0) + if (generateHeaderBinaryCompressed (os, cloud, origin, orientation)) { - PCL_ERROR ("[pcl::PCDWriter::writeBinaryCompressed] Error during open (%s)!\n", file_name.c_str()); return (-1); } -#endif - // Mandatory lock file - boost::interprocess::file_lock file_lock; - setLockingPermissions (file_name, file_lock); size_t fsize = 0; size_t data_size = 0; @@ -1600,12 +1380,21 @@ pcl::PCDWriter::writeBinaryCompressed (const std::string &file_name, const pcl:: // Compute the size of data data_size = cloud.width * cloud.height * fsize; + // If the data is too large the two 32 bit integers used to store the + // compressed and uncompressed size will overflow. + if (data_size * 3 / 2 > std::numeric_limits::max ()) + { + PCL_ERROR ("[pcl::PCDWriter::writeBinaryCompressed] The input data exceeds the maximum size for compressed version 0.7 pcds of %l bytes.\n", + static_cast (std::numeric_limits::max ()) * 2 / 3); + return (-2); + } + ////////////////////////////////////////////////////////////////////// // Empty array holding only the valid data // data_size = nr_points * point_size // = nr_points * (sizeof_field_1 + sizeof_field_2 + ... sizeof_field_n) // = sizeof_field_1 * nr_points + sizeof_field_2 * nr_points + ... sizeof_field_n * nr_points - char *only_valid_data = static_cast (malloc (data_size)); + std::vector only_valid_data (data_size); // Convert the XYZRGBXYZRGB structure to XXYYZZRGBRGB to aid compression. For // this, we need a vector of fields.size () (4 in this case), which points to @@ -1634,38 +1423,73 @@ pcl::PCDWriter::writeBinaryCompressed (const std::string &file_name, const pcl:: } } - char* temp_buf = static_cast (malloc (static_cast (static_cast (data_size) * 1.5f + 8.0f))); + std::vector temp_buf (data_size * 3 / 2 + 8); // Compress the valid data - unsigned int compressed_size = pcl::lzfCompress (only_valid_data, + unsigned int compressed_size = pcl::lzfCompress (&only_valid_data.front (), static_cast (data_size), &temp_buf[8], - static_cast (static_cast (data_size) * 1.5f)); - unsigned int compressed_final_size = 0; + data_size * 3 / 2); // Was the compression successful? - if (compressed_size) + if (compressed_size == 0) { - char *header = &temp_buf[0]; - memcpy (&header[0], &compressed_size, sizeof (unsigned int)); - memcpy (&header[4], &data_size, sizeof (unsigned int)); - data_size = compressed_size + 8; - compressed_final_size = static_cast (data_size + data_idx); + return (-1); } - else + + memcpy (&temp_buf[0], &compressed_size, 4); + memcpy (&temp_buf[4], &data_size, 4); + temp_buf.resize (compressed_size + 8); + + os.imbue (std::locale::classic ()); + os << "DATA binary_compressed\n"; + std::copy (temp_buf.begin (), temp_buf.end (), std::ostream_iterator (os)); + os.flush (); + + return (os ? 0 : -1); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +int +pcl::PCDWriter::writeBinaryCompressed (const std::string &file_name, const pcl::PCLPointCloud2 &cloud, + const Eigen::Vector4f &origin, const Eigen::Quaternionf &orientation) +{ + // Format output + std::ostringstream oss; + int status = writeBinaryCompressed (oss, cloud, origin, orientation); + if (status) { -#ifndef _WIN32 - pcl_close (fd); -#endif - resetLockingPermissions (file_name, file_lock); throw pcl::IOException ("[pcl::PCDWriter::writeBinaryCompressed] Error during compression!"); + return status; + } + std::string ostr = oss.str (); + +#ifdef _WIN32 + HANDLE h_native_file = CreateFile (file_name.c_str (), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (h_native_file == INVALID_HANDLE_VALUE) + { + PCL_ERROR ("[pcl::PCDWriter::writeBinaryCompressed] Error during CreateFile (%s)!\n", file_name.c_str ()); + return (-1); + } +#else + int fd = io::raw_open (file_name.c_str (), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd < 0) + { + PCL_ERROR ("[pcl::PCDWriter::writeBinaryCompressed] Error during open (%s)!\n", file_name.c_str ()); return (-1); } +#endif + // Mandatory lock file + boost::interprocess::file_lock file_lock; + setLockingPermissions (file_name, file_lock); #ifndef _WIN32 // Stretch the file size to the size of the data - off_t result = pcl_lseek (fd, getpagesize () + data_size - 1, SEEK_SET); + size_t page_size = getpagesize (); + size_t size_pages = ostr.size () / page_size; + size_t partial_pages = (size_pages * page_size < ostr.size ()) ? 1 : 0; + long result = io::raw_lseek (fd, (size_pages + partial_pages) * page_size - 1, SEEK_SET); if (result < 0) { - pcl_close (fd); + io::raw_close (fd); resetLockingPermissions (file_name, file_lock); PCL_ERROR ("[pcl::PCDWriter::writeBinaryCompressed] lseek errno: %d strerror: %s\n", errno, strerror (errno)); PCL_ERROR ("[pcl::PCDWriter::writeBinaryCompressed] Error during lseek ()!\n"); @@ -1675,7 +1499,7 @@ pcl::PCDWriter::writeBinaryCompressed (const std::string &file_name, const pcl:: result = static_cast (::write (fd, "", 1)); if (result != 1) { - pcl_close (fd); + io::raw_close (fd); resetLockingPermissions (file_name, file_lock); PCL_ERROR ("[pcl::PCDWriter::writeBinaryCompressed] Error during write ()!\n"); return (-1); @@ -1684,39 +1508,37 @@ pcl::PCDWriter::writeBinaryCompressed (const std::string &file_name, const pcl:: // Prepare the map #ifdef _WIN32 - HANDLE fm = CreateFileMapping (h_native_file, NULL, PAGE_READWRITE, 0, compressed_final_size, NULL); - char *map = static_cast (MapViewOfFile (fm, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, compressed_final_size)); + HANDLE fm = CreateFileMapping (h_native_file, NULL, PAGE_READWRITE, 0, ostr.size (), NULL); + char *map = static_cast (MapViewOfFile (fm, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, ostr.size ())); CloseHandle (fm); #else - char *map = static_cast (mmap (0, compressed_final_size, PROT_WRITE, MAP_SHARED, fd, 0)); + char *map = static_cast (::mmap (0, ostr.size (), PROT_WRITE, MAP_SHARED, fd, 0)); if (map == reinterpret_cast (-1)) // MAP_FAILED { - pcl_close (fd); + io::raw_close (fd); resetLockingPermissions (file_name, file_lock); PCL_ERROR ("[pcl::PCDWriter::writeBinaryCompressed] Error during mmap ()!\n"); return (-1); } #endif - // copy header - memcpy (&map[0], oss.str ().c_str (), static_cast (data_idx)); - // Copy the compressed data - memcpy (&map[data_idx], temp_buf, data_size); + // copy header + compressed data + memcpy (map, ostr.data (), ostr.size ()); #ifndef _WIN32 // If the user set the synchronization flag on, call msync if (map_synchronization_) - msync (map, compressed_final_size, MS_SYNC); + msync (map, ostr.size (), MS_SYNC); #endif // Unmap the pages of memory #ifdef _WIN32 UnmapViewOfFile (map); #else - if (munmap (map, (compressed_final_size)) == -1) + if (::munmap (map, ostr.size ()) == -1) { - pcl_close (fd); + io::raw_close (fd); resetLockingPermissions (file_name, file_lock); PCL_ERROR ("[pcl::PCDWriter::writeBinaryCompressed] Error during munmap ()!\n"); return (-1); @@ -1726,12 +1548,10 @@ pcl::PCDWriter::writeBinaryCompressed (const std::string &file_name, const pcl:: #ifdef _WIN32 CloseHandle (h_native_file); #else - pcl_close (fd); + io::raw_close (fd); #endif resetLockingPermissions (file_name, file_lock); - free (only_valid_data); - free (temp_buf); return (0); } diff --git a/io/src/ply/ply_parser.cpp b/io/src/ply/ply_parser.cpp index f678b612..1f923b1c 100644 --- a/io/src/ply/ply_parser.cpp +++ b/io/src/ply/ply_parser.cpp @@ -380,7 +380,7 @@ bool pcl::io::ply::ply_parser::parse (const std::string& filename) { if (error_callback_) { - error_callback_ (line_number_, "parse error: unkonwn scalar type"); + error_callback_ (line_number_, "parse error: unknown scalar type"); } return false; } diff --git a/io/src/ply_io.cpp b/io/src/ply_io.cpp index 2bef3b49..f7c1a959 100644 --- a/io/src/ply_io.cpp +++ b/io/src/ply_io.cpp @@ -59,7 +59,7 @@ pcl::PLYReader::elementDefinitionCallback (const std::string& element_name, std: cloud_->width = static_cast (count); cloud_->height = 1; } - cloud_->is_dense = false; + cloud_->is_dense = true; cloud_->point_step = 0; cloud_->row_step = 0; vertex_count_ = 0; @@ -95,8 +95,8 @@ pcl::PLYReader::elementDefinitionCallback (const std::string& element_name, std: bool pcl::PLYReader::endHeaderCallback () { - cloud_->data.resize (cloud_->point_step * cloud_->width * cloud_->height); - return (cloud_->data.size () == cloud_->point_step * cloud_->width * cloud_->height); + cloud_->data.resize (static_cast(cloud_->point_step) * cloud_->width * cloud_->height); + return (true); } template void @@ -267,6 +267,9 @@ namespace pcl template void PLYReader::vertexScalarPropertyCallback (Scalar value) { + if (!pcl_isfinite (value)) + cloud_->is_dense = false; + memcpy (&cloud_->data[vertex_count_ * cloud_->point_step + vertex_offset_before_], &value, sizeof (Scalar)); @@ -291,6 +294,9 @@ namespace pcl template void PLYReader::vertexListPropertyContentCallback (ContentType value) { + if (!pcl_isfinite (value)) + cloud_->is_dense = false; + memcpy (&cloud_->data[vertex_count_ * cloud_->point_step + vertex_offset_before_], &value, sizeof (ContentType)); @@ -375,20 +381,19 @@ namespace pcl void pcl::PLYReader::vertexColorCallback (const std::string& color_name, pcl::io::ply::uint8 color) { - static int32_t r, g, b; if ((color_name == "red") || (color_name == "diffuse_red")) { - r = int32_t (color); + r_ = int32_t (color); rgb_offset_before_ = vertex_offset_before_; } if ((color_name == "green") || (color_name == "diffuse_green")) { - g = int32_t (color); + g_ = int32_t (color); } if ((color_name == "blue") || (color_name == "diffuse_blue")) { - b = int32_t (color); - int32_t rgb = r << 16 | g << 8 | b; + b_ = int32_t (color); + int32_t rgb = r_ << 16 | g_ << 8 | b_; memcpy (&cloud_->data[vertex_count_ * cloud_->point_step + rgb_offset_before_], &rgb, sizeof (pcl::io::ply::float32)); @@ -399,17 +404,16 @@ pcl::PLYReader::vertexColorCallback (const std::string& color_name, pcl::io::ply void pcl::PLYReader::vertexAlphaCallback (pcl::io::ply::uint8 alpha) { - static uint32_t a, rgba; - a = uint32_t (alpha); + a_ = uint32_t (alpha); // get anscient rgb value and store it in rgba - memcpy (&rgba, + memcpy (&rgba_, &cloud_->data[vertex_count_ * cloud_->point_step + rgb_offset_before_], sizeof (pcl::io::ply::float32)); // append alpha - rgba = rgba | a << 24; + rgba_ = rgba_ | a_ << 24; // put rgba back memcpy (&cloud_->data[vertex_count_ * cloud_->point_step + rgb_offset_before_], - &rgba, + &rgba_, sizeof (uint32_t)); } @@ -519,8 +523,7 @@ pcl::PLYReader::vertexListPropertyEndCallback () {} bool pcl::PLYReader::parse (const std::string& istream_filename) { - pcl::io::ply::ply_parser::flags_type ply_parser_flags = 0; - pcl::io::ply::ply_parser ply_parser (ply_parser_flags); + pcl::io::ply::ply_parser ply_parser; ply_parser.info_callback (boost::bind (&pcl::PLYReader::infoCallback, this, boost::ref (istream_filename), _1, _2)); ply_parser.warning_callback (boost::bind (&pcl::PLYReader::warningCallback, this, boost::ref (istream_filename), _1, _2)); diff --git a/io/src/png_io.cpp b/io/src/png_io.cpp index bd3a7cc1..88ef7ebb 100644 --- a/io/src/png_io.cpp +++ b/io/src/png_io.cpp @@ -132,15 +132,3 @@ pcl::io::savePNGFile (const std::string& file_name, const pcl::PCLImage& image) PCL_ERROR ("[pcl::io::savePNGFile] Unsupported image encoding \"%s\".\n", image.encoding.c_str ()); } } - -void -pcl::io::savePNGFile (const std::string& file_name, const pcl::PointCloud& cloud) -{ - std::vector data(cloud.width * cloud.height); - for (size_t i = 0; i < cloud.points.size (); ++i) - { - data[i] = static_cast (cloud.points[i].label); - } - saveShortPNGFile(file_name, &data[0], cloud.width, cloud.height,1); -} - diff --git a/io/src/real_sense_grabber.cpp b/io/src/real_sense_grabber.cpp index 14a26882..2b429a3f 100644 --- a/io/src/real_sense_grabber.cpp +++ b/io/src/real_sense_grabber.cpp @@ -323,7 +323,7 @@ pcl::RealSenseGrabber::run () frequency_.event (); fps_mutex_.unlock (); - /* We preform the following steps to convert received data into point clouds: + /* We perform the following steps to convert received data into point clouds: * * 1. Push depth image to the depth buffer * 2. Pull filtered depth image from the depth buffer diff --git a/io/src/vlp_grabber.cpp b/io/src/vlp_grabber.cpp index ec0b64bf..f996f20d 100644 --- a/io/src/vlp_grabber.cpp +++ b/io/src/vlp_grabber.cpp @@ -40,21 +40,20 @@ using boost::asio::ip::udp; -#define VLP_MAX_NUM_LASERS 16 -#define VLP_DUAL_MODE 0x39 - ///////////////////////////////////////////////////////////////////////////// pcl::VLPGrabber::VLPGrabber (const std::string& pcapFile) : HDLGrabber ("", pcapFile) { + initializeLaserMapping (); loadVLP16Corrections (); } ///////////////////////////////////////////////////////////////////////////// pcl::VLPGrabber::VLPGrabber (const boost::asio::ip::address& ipAddress, - const unsigned short int port) : + const uint16_t port) : HDLGrabber (ipAddress, port) { + initializeLaserMapping (); loadVLP16Corrections (); } @@ -65,12 +64,21 @@ pcl::VLPGrabber::~VLPGrabber () throw () ///////////////////////////////////////////////////////////////////////////// void -pcl::VLPGrabber::loadVLP16Corrections () +pcl::VLPGrabber::initializeLaserMapping () { - double vlp16_vertical_corrections[] = { -15, 1, -13, 3, -11, 5, -9, 7, -7, 9, -5, 11, -3, 13, -1, 15 + for (uint8_t i = 0; i < VLP_MAX_NUM_LASERS / 2u; i++) + { + laser_rgb_mapping_[i * 2].b = static_cast (i * 6 + 64); + laser_rgb_mapping_[i * 2 + 1].b = static_cast ((i + 16) * 6 + 64); + } +} - }; - for (int i = 0; i < VLP_MAX_NUM_LASERS; i++) +///////////////////////////////////////////////////////////////////////////// +void +pcl::VLPGrabber::loadVLP16Corrections () +{ + double vlp16_vertical_corrections[] = { -15, 1, -13, 3, -11, 5, -9, 7, -7, 9, -5, 11, -3, 13, -1, 15 }; + for (uint8_t i = 0; i < VLP_MAX_NUM_LASERS; i++) { HDLGrabber::laser_corrections_[i].azimuthCorrection = 0.0; HDLGrabber::laser_corrections_[i].distanceCorrection = 0.0; @@ -106,25 +114,25 @@ pcl::VLPGrabber::toPointClouds (HDLDataPacket *dataPacket) double interpolated_azimuth_delta; - int index = 1; + uint8_t index = 1; if (dataPacket->mode == VLP_DUAL_MODE) { index = 2; } if (dataPacket->firingData[index].rotationalPosition < dataPacket->firingData[0].rotationalPosition) { - interpolated_azimuth_delta = ( (dataPacket->firingData[index].rotationalPosition + 36000) - dataPacket->firingData[0].rotationalPosition) / 2.0; + interpolated_azimuth_delta = ((dataPacket->firingData[index].rotationalPosition + 36000) - dataPacket->firingData[0].rotationalPosition) / 2.0; } else { interpolated_azimuth_delta = (dataPacket->firingData[index].rotationalPosition - dataPacket->firingData[0].rotationalPosition) / 2.0; } - for (int i = 0; i < HDL_FIRING_PER_PKT; ++i) + for (uint8_t i = 0; i < HDL_FIRING_PER_PKT; ++i) { HDLFiringData firing_data = dataPacket->firingData[i]; - for (int j = 0; j < HDL_LASER_PER_FIRING; j++) + for (uint8_t j = 0; j < HDL_LASER_PER_FIRING; j++) { double current_azimuth = firing_data.rotationalPosition; if (j >= VLP_MAX_NUM_LASERS) @@ -139,10 +147,12 @@ pcl::VLPGrabber::toPointClouds (HDLDataPacket *dataPacket) { if (current_sweep_xyz_->size () > 0) { - current_sweep_xyz_->is_dense = current_sweep_xyzi_->is_dense = false; + current_sweep_xyz_->is_dense = current_sweep_xyzrgba_->is_dense = current_sweep_xyzi_->is_dense = false; current_sweep_xyz_->header.stamp = velodyne_time; + current_sweep_xyzrgba_->header.stamp = velodyne_time; current_sweep_xyzi_->header.stamp = velodyne_time; current_sweep_xyz_->header.seq = sweep_counter; + current_sweep_xyzrgba_->header.seq = sweep_counter; current_sweep_xyzi_->header.seq = sweep_counter; sweep_counter++; @@ -150,43 +160,51 @@ pcl::VLPGrabber::toPointClouds (HDLDataPacket *dataPacket) HDLGrabber::fireCurrentSweep (); } current_sweep_xyz_.reset (new pcl::PointCloud ()); + current_sweep_xyzrgba_.reset (new pcl::PointCloud ()); current_sweep_xyzi_.reset (new pcl::PointCloud ()); } PointXYZ xyz; PointXYZI xyzi; + PointXYZRGBA xyzrgba; PointXYZ dual_xyz; PointXYZI dual_xyzi; + PointXYZRGBA dual_xyzrgba; HDLGrabber::computeXYZI (xyzi, current_azimuth, firing_data.laserReturns[j], laser_corrections_[j % VLP_MAX_NUM_LASERS]); - xyz.x = xyzi.x; - xyz.y = xyzi.y; - xyz.z = xyzi.z; + xyz.x = xyzrgba.x = xyzi.x; + xyz.y = xyzrgba.y = xyzi.y; + xyz.z = xyzrgba.z = xyzi.z; + + xyzrgba.rgba = laser_rgb_mapping_[j % VLP_MAX_NUM_LASERS].rgba; if (dataPacket->mode == VLP_DUAL_MODE) { HDLGrabber::computeXYZI (dual_xyzi, current_azimuth, dataPacket->firingData[i + 1].laserReturns[j], laser_corrections_[j % VLP_MAX_NUM_LASERS]); - dual_xyz.x = dual_xyzi.x; - dual_xyz.y = dual_xyzi.y; - dual_xyz.z = dual_xyzi.z; + dual_xyz.x = dual_xyzrgba.x = dual_xyzi.x; + dual_xyz.y = dual_xyzrgba.y = dual_xyzi.y; + dual_xyz.z = dual_xyzrgba.z = dual_xyzi.z; + dual_xyzrgba.rgba = laser_rgb_mapping_[j % VLP_MAX_NUM_LASERS].rgba; } if (! (pcl_isnan (xyz.x) || pcl_isnan (xyz.y) || pcl_isnan (xyz.z))) { current_sweep_xyz_->push_back (xyz); + current_sweep_xyzrgba_->push_back (xyzrgba); current_sweep_xyzi_->push_back (xyzi); last_azimuth_ = current_azimuth; } if (dataPacket->mode == VLP_DUAL_MODE) { - if ( (dual_xyz.x != xyz.x || dual_xyz.y != xyz.y || dual_xyz.z != xyz.z) + if ((dual_xyz.x != xyz.x || dual_xyz.y != xyz.y || dual_xyz.z != xyz.z) && ! (pcl_isnan (dual_xyz.x) || pcl_isnan (dual_xyz.y) || pcl_isnan (dual_xyz.z))) { current_sweep_xyz_->push_back (dual_xyz); + current_sweep_xyzrgba_->push_back (dual_xyzrgba); current_sweep_xyzi_->push_back (dual_xyzi); } } @@ -205,3 +223,20 @@ pcl::VLPGrabber::getName () const return (std::string ("Velodyne LiDAR (VLP) Grabber")); } +///////////////////////////////////////////////////////////////////////////// +void +pcl::VLPGrabber::setLaserColorRGB (const pcl::RGB& color, + const uint8_t laserNumber) +{ + if (laserNumber >= VLP_MAX_NUM_LASERS) + return; + + laser_rgb_mapping_[laserNumber] = color; +} + +///////////////////////////////////////////////////////////////////////////// +uint8_t +pcl::VLPGrabber::getMaximumNumberOfLasers () const +{ + return (VLP_MAX_NUM_LASERS); +} diff --git a/io/src/vtk_lib_io.cpp b/io/src/vtk_lib_io.cpp index cc537c15..92a880a7 100644 --- a/io/src/vtk_lib_io.cpp +++ b/io/src/vtk_lib_io.cpp @@ -288,17 +288,21 @@ pcl::io::vtk2mesh (const vtkSmartPointer& poly_data, pcl::PolygonMe // Then the color information, if any vtkUnsignedCharArray* poly_colors = NULL; if (poly_data->GetPointData() != NULL) + { poly_colors = vtkUnsignedCharArray::SafeDownCast (poly_data->GetPointData ()->GetScalars ("Colors")); - // some applications do not save the name of scalars (including PCL's native vtk_io) - if (!poly_colors && poly_data->GetPointData () != NULL) - poly_colors = vtkUnsignedCharArray::SafeDownCast (poly_data->GetPointData ()->GetScalars ("scalars")); + // some applications do not save the name of scalars (including PCL's native vtk_io) + if (!poly_colors) + poly_colors = vtkUnsignedCharArray::SafeDownCast (poly_data->GetPointData ()->GetScalars ("scalars")); + + if (!poly_colors) + poly_colors = vtkUnsignedCharArray::SafeDownCast (poly_data->GetPointData ()->GetScalars ("RGB")); - if (!poly_colors && poly_data->GetPointData () != NULL) - poly_colors = vtkUnsignedCharArray::SafeDownCast (poly_data->GetPointData ()->GetScalars ("RGB")); + if (!poly_colors) + poly_colors = vtkUnsignedCharArray::SafeDownCast (poly_data->GetPointData ()->GetScalars ("RGBA")); + } - // TODO: currently only handles rgb values with 3 components - if (poly_colors && (poly_colors->GetNumberOfComponents () == 3)) + if (poly_colors && (poly_colors->GetNumberOfComponents () == 3 || poly_colors->GetNumberOfComponents () == 4)) { pcl::PointCloud::Ptr rgb_cloud (new pcl::PointCloud ()); rgb_cloud->points.resize (nr_points); @@ -306,13 +310,15 @@ pcl::io::vtk2mesh (const vtkSmartPointer& poly_data, pcl::PolygonMe rgb_cloud->height = 1; rgb_cloud->is_dense = true; - unsigned char point_color[3]; + unsigned char point_color[4] = {0, 0, 0, 255}; for (vtkIdType i = 0; i < mesh_points->GetNumberOfPoints (); i++) { poly_colors->GetTupleValue (i, &point_color[0]); - rgb_cloud->points[i].r = point_color[0]; - rgb_cloud->points[i].g = point_color[1]; - rgb_cloud->points[i].b = point_color[2]; + // individual component copy due to different memory layout + (*rgb_cloud)[i].r = point_color[0]; + (*rgb_cloud)[i].g = point_color[1]; + (*rgb_cloud)[i].b = point_color[2]; + (*rgb_cloud)[i].a = point_color[3]; } pcl::PCLPointCloud2 rgb_cloud2; diff --git a/io/tools/converter.cpp b/io/tools/converter.cpp index 8c0b6ee3..64084863 100644 --- a/io/tools/converter.cpp +++ b/io/tools/converter.cpp @@ -63,7 +63,7 @@ * @param argv[in] */ void -displayHelp (int argc, +displayHelp (int, char** argv) { PCL_INFO ("\nUsage: %s [OPTION] SOURCE DEST\n", argv[0]); diff --git a/io/tools/openni_pcd_recorder.cpp b/io/tools/openni_pcd_recorder.cpp index 1174498b..82358653 100644 --- a/io/tools/openni_pcd_recorder.cpp +++ b/io/tools/openni_pcd_recorder.cpp @@ -298,7 +298,7 @@ class Consumer { boost::mutex::scoped_lock io_lock (io_mutex); - print_info ("Writing remaing %ld clouds in the buffer to disk...\n", buf_.getSize ()); + print_info ("Writing remaining %ld clouds in the buffer to disk...\n", buf_.getSize ()); } while (!buf_.isEmpty ()) writeToDisk (buf_.getFront ()); diff --git a/io/tools/ply/ply2obj.cpp b/io/tools/ply/ply2obj.cpp index 6ae74ba9..0c3ef0ca 100644 --- a/io/tools/ply/ply2obj.cpp +++ b/io/tools/ply/ply2obj.cpp @@ -294,8 +294,7 @@ ply_to_obj_converter::face_end () bool ply_to_obj_converter::convert (std::istream&, const std::string& istream_filename, std::ostream& ostream, const std::string&) { - pcl::io::ply::ply_parser::flags_type ply_parser_flags = 0; - pcl::io::ply::ply_parser ply_parser (ply_parser_flags); + pcl::io::ply::ply_parser ply_parser; ply_parser.info_callback (boost::bind (&ply_to_obj_converter::info_callback, this, boost::ref (istream_filename), _1, _2)); ply_parser.warning_callback (boost::bind (&ply_to_obj_converter::warning_callback, this, boost::ref (istream_filename), _1, _2)); diff --git a/io/tools/ply/ply2ply.cpp b/io/tools/ply/ply2ply.cpp index 62c5d57e..0580ea69 100644 --- a/io/tools/ply/ply2ply.cpp +++ b/io/tools/ply/ply2ply.cpp @@ -339,9 +339,7 @@ ply_to_ply_converter::end_header_callback() bool ply_to_ply_converter::convert (const std::string &ifilename, std::istream&, std::ostream& ostream) { - pcl::io::ply::ply_parser::flags_type ply_parser_flags = 0; - - pcl::io::ply::ply_parser ply_parser(ply_parser_flags); + pcl::io::ply::ply_parser ply_parser; ply_parser.info_callback(boost::bind(&ply_to_ply_converter::info_callback, this, boost::ref(ifilename), _1, _2)); ply_parser.warning_callback(boost::bind(&ply_to_ply_converter::warning_callback, this, boost::ref(ifilename), _1, _2)); diff --git a/io/tools/ply/ply2raw.cpp b/io/tools/ply/ply2raw.cpp index 496b771a..5e8b9250 100644 --- a/io/tools/ply/ply2raw.cpp +++ b/io/tools/ply/ply2raw.cpp @@ -303,8 +303,7 @@ ply_to_raw_converter::face_end () {} bool ply_to_raw_converter::convert (std::istream&, const std::string& istream_filename, std::ostream& ostream, const std::string&) { - pcl::io::ply::ply_parser::flags_type ply_parser_flags = 0; - pcl::io::ply::ply_parser ply_parser (ply_parser_flags); + pcl::io::ply::ply_parser ply_parser; ply_parser.info_callback (boost::bind (&ply_to_raw_converter::info_callback, this, boost::ref (istream_filename), _1, _2)); ply_parser.warning_callback (boost::bind (&ply_to_raw_converter::warning_callback, this, boost::ref (istream_filename), _1, _2)); diff --git a/kdtree/include/pcl/kdtree/impl/kdtree_flann.hpp b/kdtree/include/pcl/kdtree/impl/kdtree_flann.hpp index 4777f8ee..a6a1af2c 100644 --- a/kdtree/include/pcl/kdtree/impl/kdtree_flann.hpp +++ b/kdtree/include/pcl/kdtree/impl/kdtree_flann.hpp @@ -58,7 +58,7 @@ pcl::KdTreeFLANN::KdTreeFLANN (bool sorted) /////////////////////////////////////////////////////////////////////////////////////////// template -pcl::KdTreeFLANN::KdTreeFLANN (const KdTreeFLANN &k) +pcl::KdTreeFLANN::KdTreeFLANN (const KdTreeFLANN &k) : pcl::KdTree (false) , flann_index_ (), cloud_ () , index_mapping_ (), identity_mapping_ (false) diff --git a/kdtree/include/pcl/kdtree/kdtree_flann.h b/kdtree/include/pcl/kdtree/kdtree_flann.h index 3cf5d9da..448a937a 100644 --- a/kdtree/include/pcl/kdtree/kdtree_flann.h +++ b/kdtree/include/pcl/kdtree/kdtree_flann.h @@ -86,8 +86,8 @@ namespace pcl typedef ::flann::Index FLANNIndex; // Boost shared pointers - typedef boost::shared_ptr > Ptr; - typedef boost::shared_ptr > ConstPtr; + typedef boost::shared_ptr > Ptr; + typedef boost::shared_ptr > ConstPtr; /** \brief Default Constructor for KdTreeFLANN. * \param[in] sorted set to true if the application that the tree will be used for requires sorted nearest neighbor indices (default). False otherwise. @@ -99,13 +99,13 @@ namespace pcl /** \brief Copy constructor * \param[in] k the tree to copy into this */ - KdTreeFLANN (const KdTreeFLANN &k); + KdTreeFLANN (const KdTreeFLANN &k); /** \brief Copy operator * \param[in] k the tree to copy into this */ - inline KdTreeFLANN& - operator = (const KdTreeFLANN& k) + inline KdTreeFLANN& + operator = (const KdTreeFLANN& k) { KdTree::operator=(k); flann_index_ = k.flann_index_; @@ -128,7 +128,7 @@ namespace pcl void setSortedResults (bool sorted); - inline Ptr makeShared () { return Ptr (new KdTreeFLANN (*this)); } + inline Ptr makeShared () { return Ptr (new KdTreeFLANN (*this)); } /** \brief Destructor for KdTreeFLANN. * Deletes all allocated data arrays and destroys the kd-tree structures. diff --git a/keypoints/CMakeLists.txt b/keypoints/CMakeLists.txt index bb5a6acf..94dbd861 100644 --- a/keypoints/CMakeLists.txt +++ b/keypoints/CMakeLists.txt @@ -19,6 +19,8 @@ if(build) src/susan.cpp src/iss_3d.cpp src/brisk_2d.cpp + src/trajkovic_2d.cpp + src/trajkovic_3d.cpp ) set(incs "include/pcl/${SUBSYS_NAME}/keypoint.h" @@ -33,6 +35,8 @@ if(build) "include/pcl/${SUBSYS_NAME}/susan.h" "include/pcl/${SUBSYS_NAME}/iss_3d.h" "include/pcl/${SUBSYS_NAME}/brisk_2d.h" + "include/pcl/${SUBSYS_NAME}/trajkovic_2d.h" + "include/pcl/${SUBSYS_NAME}/trajkovic_3d.h" ) set(impl_incs "include/pcl/${SUBSYS_NAME}/impl/keypoint.hpp" @@ -45,6 +49,8 @@ if(build) "include/pcl/${SUBSYS_NAME}/impl/susan.hpp" "include/pcl/${SUBSYS_NAME}/impl/iss_3d.hpp" "include/pcl/${SUBSYS_NAME}/impl/brisk_2d.hpp" + "include/pcl/${SUBSYS_NAME}/impl/trajkovic_2d.hpp" + "include/pcl/${SUBSYS_NAME}/impl/trajkovic_3d.hpp" ) set(LIB_NAME "pcl_${SUBSYS_NAME}") diff --git a/keypoints/include/pcl/keypoints/agast_2d.h b/keypoints/include/pcl/keypoints/agast_2d.h index 051fc566..a5779f9d 100644 --- a/keypoints/include/pcl/keypoints/agast_2d.h +++ b/keypoints/include/pcl/keypoints/agast_2d.h @@ -159,7 +159,7 @@ namespace pcl nr_max_keypoints_ = nr_max_keypoints; } - /** \brief Get the maximum nuber of keypoints to return, as set by the user. */ + /** \brief Get the maximum number of keypoints to return, as set by the user. */ inline unsigned int getMaxKeypoints () { @@ -641,7 +641,7 @@ namespace pcl nr_max_keypoints_ = nr_max_keypoints; } - /** \brief Get the maximum nuber of keypoints to return, as set by the user. */ + /** \brief Get the maximum number of keypoints to return, as set by the user. */ inline unsigned int getMaxKeypoints () { diff --git a/keypoints/include/pcl/keypoints/harris_2d.h b/keypoints/include/pcl/keypoints/harris_2d.h index cfe08a55..a9663070 100644 --- a/keypoints/include/pcl/keypoints/harris_2d.h +++ b/keypoints/include/pcl/keypoints/harris_2d.h @@ -160,7 +160,7 @@ namespace pcl bool refine_; /// non maximas suppression bool nonmax_; - /// cornerness computation methode + /// cornerness computation method ResponseMethod method_; /// number of threads to be used unsigned int threads_; diff --git a/keypoints/include/pcl/keypoints/impl/trajkovic_3d.hpp b/keypoints/include/pcl/keypoints/impl/trajkovic_3d.hpp index 4e3f7d6e..8e3e6854 100644 --- a/keypoints/include/pcl/keypoints/impl/trajkovic_3d.hpp +++ b/keypoints/include/pcl/keypoints/impl/trajkovic_3d.hpp @@ -234,9 +234,9 @@ pcl::TrajkovicKeypoint3D::detectKeypoints (PointCl #ifdef _OPENMP #pragma omp parallel for shared (output) num_threads (threads_) #endif - for (size_t i = 0; i < indices.size (); ++i) + for (int i = 0; i < static_cast(indices.size ()); ++i) { - int idx = indices[i]; + int idx = indices[static_cast(i)]; if ((response_->points[idx] < second_threshold_) || occupency_map[idx]) continue; diff --git a/keypoints/include/pcl/keypoints/susan.h b/keypoints/include/pcl/keypoints/susan.h index 1613f471..3a833e7e 100644 --- a/keypoints/include/pcl/keypoints/susan.h +++ b/keypoints/include/pcl/keypoints/susan.h @@ -44,7 +44,7 @@ namespace pcl { - /** \brief SUSANKeypoint implements a RGB-D extension of the SUSAN detector inluding normal + /** \brief SUSANKeypoint implements a RGB-D extension of the SUSAN detector including normal * directions variation in top of intensity variation. * It is different from Harris in that it exploits normals directly so it is faster. * Original paper "SUSAN — A New Approach to Low Level Image Processing", Smith, diff --git a/ml/CMakeLists.txt b/ml/CMakeLists.txt index 84884933..cb572d2f 100644 --- a/ml/CMakeLists.txt +++ b/ml/CMakeLists.txt @@ -10,16 +10,6 @@ PCL_ADD_DOC("${SUBSYS_NAME}") if(build) set(incs - "include/pcl/${SUBSYS_NAME}/dt/decision_forest.h" - "include/pcl/${SUBSYS_NAME}/dt/decision_forest_evaluator.h" - "include/pcl/${SUBSYS_NAME}/dt/decision_forest_trainer.h" - "include/pcl/${SUBSYS_NAME}/dt/decision_tree.h" - "include/pcl/${SUBSYS_NAME}/dt/decision_tree_evaluator.h" - "include/pcl/${SUBSYS_NAME}/dt/decision_tree_trainer.h" - "include/pcl/${SUBSYS_NAME}/dt/decision_tree_data_provider.h" - "include/pcl/${SUBSYS_NAME}/ferns/fern.h" - "include/pcl/${SUBSYS_NAME}/ferns/fern_evaluator.h" - "include/pcl/${SUBSYS_NAME}/ferns/fern_trainer.h" "include/pcl/${SUBSYS_NAME}/branch_estimator.h" "include/pcl/${SUBSYS_NAME}/feature_handler.h" "include/pcl/${SUBSYS_NAME}/multi_channel_2d_comparison_feature.h" @@ -42,15 +32,36 @@ if(build) "include/pcl/${SUBSYS_NAME}/kmeans.h" ) - set(impl_incs + set(dt_incs + "include/pcl/${SUBSYS_NAME}/dt/decision_forest.h" + "include/pcl/${SUBSYS_NAME}/dt/decision_forest_evaluator.h" + "include/pcl/${SUBSYS_NAME}/dt/decision_forest_trainer.h" + "include/pcl/${SUBSYS_NAME}/dt/decision_tree.h" + "include/pcl/${SUBSYS_NAME}/dt/decision_tree_evaluator.h" + "include/pcl/${SUBSYS_NAME}/dt/decision_tree_trainer.h" + "include/pcl/${SUBSYS_NAME}/dt/decision_tree_data_provider.h" + ) + + set(ferns_incs + "include/pcl/${SUBSYS_NAME}/ferns/fern.h" + "include/pcl/${SUBSYS_NAME}/ferns/fern_evaluator.h" + "include/pcl/${SUBSYS_NAME}/ferns/fern_trainer.h" + ) + + set(dt_impl_incs "include/pcl/${SUBSYS_NAME}/impl/dt/decision_forest_evaluator.hpp" "include/pcl/${SUBSYS_NAME}/impl/dt/decision_forest_trainer.hpp" "include/pcl/${SUBSYS_NAME}/impl/dt/decision_tree_evaluator.hpp" "include/pcl/${SUBSYS_NAME}/impl/dt/decision_tree_trainer.hpp" + ) + + set(ferns_impl_incs "include/pcl/${SUBSYS_NAME}/impl/ferns/fern_evaluator.hpp" "include/pcl/${SUBSYS_NAME}/impl/ferns/fern_trainer.hpp" + ) + + set(svm_impl_incs "include/pcl/${SUBSYS_NAME}/impl/svm/svm_wrapper.hpp" - #"include/pcl/${SUBSYS_NAME}/impl/kmeans.hpp" ) set(srcs @@ -76,5 +87,10 @@ if(build) PCL_MAKE_PKGCONFIG("${LIB_NAME}" "${SUBSYS_NAME}" "${SUBSYS_DESC}" "${SUBSYS_DEPS}" "" "" "" "") # Install include files PCL_ADD_INCLUDES("${SUBSYS_NAME}" "${SUBSYS_NAME}" ${incs}) + PCL_ADD_INCLUDES("${SUBSYS_NAME}" "${SUBSYS_NAME}/dt" ${dt_incs}) + PCL_ADD_INCLUDES("${SUBSYS_NAME}" "${SUBSYS_NAME}/ferns" ${ferns_incs}) + PCL_ADD_INCLUDES("${SUBSYS_NAME}" "${SUBSYS_NAME}/impl/dt" ${dt_impl_incs}) + PCL_ADD_INCLUDES("${SUBSYS_NAME}" "${SUBSYS_NAME}/impl/ferns" ${ferns_impl_incs}) + PCL_ADD_INCLUDES("${SUBSYS_NAME}" "${SUBSYS_NAME}/impl/svm" ${svm_impl_incs}) endif(build) diff --git a/ml/include/pcl/ml/feature_handler.h b/ml/include/pcl/ml/feature_handler.h index 10d97df5..ed5309f5 100644 --- a/ml/include/pcl/ml/feature_handler.h +++ b/ml/include/pcl/ml/feature_handler.h @@ -96,7 +96,7 @@ namespace pcl /** \brief Generates evaluation code for the specified feature and writes it to the specified stream. * \param[in] feature The feature for which code is generated. - * \param[out] stream The destionation for the code. + * \param[out] stream The destination for the code. */ virtual void generateCodeForEvaluation (const FeatureType & feature, diff --git a/ml/include/pcl/ml/impl/kmeans.hpp b/ml/include/pcl/ml/impl/kmeans.hpp index 5afe1188..79449e4b 100644 --- a/ml/include/pcl/ml/impl/kmeans.hpp +++ b/ml/include/pcl/ml/impl/kmeans.hpp @@ -132,7 +132,7 @@ pcl::Kmeans::cluster (std::vector &clusters) } - // if cluster field name is set, check if field name is valied + // if cluster field name is set, check if field name is valid else { user_index = pcl::getFieldIndex (point, cluster_field_name_.c_str (), fields); diff --git a/ml/include/pcl/ml/regression_variance_stats_estimator.h b/ml/include/pcl/ml/regression_variance_stats_estimator.h index c7b94d40..ef7c2478 100644 --- a/ml/include/pcl/ml/regression_variance_stats_estimator.h +++ b/ml/include/pcl/ml/regression_variance_stats_estimator.h @@ -156,7 +156,7 @@ namespace pcl * \param[in] data_set The data set corresponding to the supplied result data. * \param[in] examples The examples used for extracting the supplied result data. * \param[in] label_data The label data corresponding to the specified examples. - * \param[in] results The results computed using the specifed examples. + * \param[in] results The results computed using the specified examples. * \param[in] flags The flags corresponding to the results. * \param[in] threshold The threshold for which the information gain is computed. */ diff --git a/ml/include/pcl/ml/stats_estimator.h b/ml/include/pcl/ml/stats_estimator.h index 91c8e3ed..374e1334 100644 --- a/ml/include/pcl/ml/stats_estimator.h +++ b/ml/include/pcl/ml/stats_estimator.h @@ -124,7 +124,7 @@ namespace pcl /** \brief Generates code for computing the branch indices for the specified node and writes it to the specified stream. * \param[in] node The node for which the branch index estimation code is generated. - * \param[out] stream The destionation for the code. + * \param[out] stream The destination for the code. */ virtual void generateCodeForBranchIndexComputation (NodeType & node, @@ -132,7 +132,7 @@ namespace pcl /** \brief Generates code for computing the output for the specified node and writes it to the specified stream. * \param[in] node The node for which the output estimation code is generated. - * \param[out] stream The destionation for the code. + * \param[out] stream The destination for the code. */ virtual void generateCodeForOutput (NodeType & node, diff --git a/ml/include/pcl/ml/svm_wrapper.h b/ml/include/pcl/ml/svm_wrapper.h index 960afaa3..6ddc7ed7 100755 --- a/ml/include/pcl/ml/svm_wrapper.h +++ b/ml/include/pcl/ml/svm_wrapper.h @@ -87,7 +87,7 @@ namespace pcl } }; - /** \brief The structure initialize a model crated by the SVM (Support Vector Machines) classifier (pcl::SVMTrain) + /** \brief The structure initialize a model created by the SVM (Support Vector Machines) classifier (pcl::SVMTrain) */ struct SVMModel: svm_model { diff --git a/ml/src/kmeans.cpp b/ml/src/kmeans.cpp index 9cfcbaee..8d476e4b 100644 --- a/ml/src/kmeans.cpp +++ b/ml/src/kmeans.cpp @@ -106,13 +106,13 @@ pcl::Kmeans::computeCentroids() { num_points_in_cluster = 0; - // For earch PointId in this set + // For each PointId in this set BOOST_FOREACH(SetPoints::value_type pid, clusters_to_points_[cid]) { Point p = data_[pid]; //Point p = ps__.getPoint(pid); for (i=0; i &clusters) /* } - // if cluster field name is set, check if field name is valied + // if cluster field name is set, check if field name is valid else { user_index = pcl::getFieldIndex (point, cluster_field_name_.c_str (), fields); diff --git a/ml/src/svm.cpp b/ml/src/svm.cpp index dcd6504a..e9c162d0 100755 --- a/ml/src/svm.cpp +++ b/ml/src/svm.cpp @@ -1981,7 +1981,7 @@ static decision_function svm_train_one ( return f; } -// Platt's binary SVM Probablistic Output: an improvement from Lin et al. +// Platt's binary SVM Probabilistic Output: an improvement from Lin et al. static void sigmoid_train ( int l, const double *dec_values, const double *labels, double& A, double& B) diff --git a/octree/CMakeLists.txt b/octree/CMakeLists.txt index f4505b12..c79f7460 100644 --- a/octree/CMakeLists.txt +++ b/octree/CMakeLists.txt @@ -46,7 +46,7 @@ if(build) set(LIB_NAME "pcl_${SUBSYS_NAME}") PCL_ADD_LIBRARY("${LIB_NAME}" "${SUBSYS_NAME}" ${srcs} ${incs} ${impl_incs}) - target_link_libraries("${LIB_NAME}") + target_link_libraries("${LIB_NAME}" pcl_common) target_include_directories(${LIB_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") PCL_MAKE_PKGCONFIG("${LIB_NAME}" "${SUBSYS_NAME}" "${SUBSYS_DESC}" "${SUBSYS_DEPS}" "" "" "" "") diff --git a/octree/include/pcl/octree/impl/octree2buf_base.hpp b/octree/include/pcl/octree/impl/octree2buf_base.hpp index 522ddfe4..28f5d774 100644 --- a/octree/include/pcl/octree/impl/octree2buf_base.hpp +++ b/octree/include/pcl/octree/impl/octree2buf_base.hpp @@ -364,7 +364,7 @@ namespace pcl child_branch = static_cast (child_node); branch_arg->setChildPtr(buffer_selector_, child_idx, child_node); } else { - // depth has changed.. child in preceeding buffer is a leaf node. + // depth has changed.. child in preceding buffer is a leaf node. deleteBranchChild (*branch_arg, !buffer_selector_, child_idx); child_branch = createBranchChild (*branch_arg, child_idx); } @@ -406,7 +406,7 @@ namespace pcl child_leaf = static_cast (child_node); branch_arg->setChildPtr(buffer_selector_, child_idx, child_node); } else { - // depth has changed.. child in preceeding buffer is a leaf node. + // depth has changed.. child in preceding buffer is a leaf node. deleteBranchChild (*branch_arg, !buffer_selector_, child_idx); child_leaf = createLeafChild (*branch_arg, child_idx); } @@ -697,7 +697,7 @@ namespace pcl child_branch = static_cast (child_node); branch_arg->setChildPtr(buffer_selector_, child_idx, child_node); } else { - // depth has changed.. child in preceeding buffer is a leaf node. + // depth has changed.. child in preceding buffer is a leaf node. deleteBranchChild (*branch_arg, !buffer_selector_, child_idx); child_branch = createBranchChild (*branch_arg, child_idx); } @@ -741,7 +741,7 @@ namespace pcl child_leaf = static_cast (child_node); branch_arg->setChildPtr(buffer_selector_, child_idx, child_node); } else { - // depth has changed.. child in preceeding buffer is a leaf node. + // depth has changed.. child in preceding buffer is a leaf node. deleteBranchChild (*branch_arg, !buffer_selector_, child_idx); child_leaf = createLeafChild (*branch_arg, child_idx); } diff --git a/octree/include/pcl/octree/impl/octree_base.hpp b/octree/include/pcl/octree/impl/octree_base.hpp index 77712853..fbca6f2b 100644 --- a/octree/include/pcl/octree/impl/octree_base.hpp +++ b/octree/include/pcl/octree/impl/octree_base.hpp @@ -79,7 +79,7 @@ namespace pcl assert(max_voxel_index_arg>0); // tree depth == bitlength of maxVoxels - tree_depth = std::max ( (std::min (static_cast (OctreeKey::maxDepth), static_cast (std::ceil (Log2 (max_voxel_index_arg))))), 0u); + tree_depth = std::min (static_cast (OctreeKey::maxDepth), static_cast (std::ceil (Log2 (max_voxel_index_arg)))); // define depthMask_ by setting a single bit to 1 at bit position == tree depth depth_mask_ = (1 << (tree_depth - 1)); diff --git a/octree/include/pcl/octree/impl/octree_iterator.hpp b/octree/include/pcl/octree/impl/octree_iterator.hpp index 6811ad4a..b089689d 100644 --- a/octree/include/pcl/octree/impl/octree_iterator.hpp +++ b/octree/include/pcl/octree/impl/octree_iterator.hpp @@ -39,6 +39,8 @@ #ifndef PCL_OCTREE_ITERATOR_HPP_ #define PCL_OCTREE_ITERATOR_HPP_ +#include + namespace pcl { namespace octree @@ -61,12 +63,6 @@ namespace pcl this->reset (); } - ////////////////////////////////////////////////////////////////////////////////////////////// - template - OctreeDepthFirstIterator::~OctreeDepthFirstIterator () - { - } - ////////////////////////////////////////////////////////////////////////////////////////////// template void OctreeDepthFirstIterator::reset () @@ -137,18 +133,16 @@ namespace pcl if ( (this->max_octree_depth_>=stack_entry.depth_) && (stack_entry.node_->getNodeType () == BRANCH_NODE) ) { - unsigned char child_idx; - // current node is a branch node BranchNode* current_branch = static_cast (stack_entry.node_); // add all children to stack - for (child_idx = 0; child_idx < 8; ++child_idx) + for (int8_t i = 7; i >= 0; --i) { + const unsigned char child_idx = (unsigned char) i; // if child exist - if (this->octree_->branchHasChild(*current_branch, child_idx)) { // add child to stack @@ -197,12 +191,6 @@ namespace pcl this->reset (); } - ////////////////////////////////////////////////////////////////////////////////////////////// - template - OctreeBreadthFirstIterator::~OctreeBreadthFirstIterator () - { - } - ////////////////////////////////////////////////////////////////////////////////////////////// template void OctreeBreadthFirstIterator::reset () @@ -281,6 +269,117 @@ namespace pcl return (*this); } + + ////////////////////////////////////////////////////////////////////////////////////////////// + template + OctreeFixedDepthIterator::OctreeFixedDepthIterator () : + OctreeBreadthFirstIterator (0u), fixed_depth_ (0u) + {} + + ////////////////////////////////////////////////////////////////////////////////////////////// + template + OctreeFixedDepthIterator::OctreeFixedDepthIterator (OctreeT* octree_arg, unsigned int fixed_depth_arg) : + OctreeBreadthFirstIterator (octree_arg, fixed_depth_arg), fixed_depth_ (fixed_depth_arg) + { + this->reset (fixed_depth_arg); + } + + ////////////////////////////////////////////////////////////////////////////////////////////// + template + void OctreeFixedDepthIterator::reset (unsigned int fixed_depth_arg) + { + // Set the desired depth to walk through + fixed_depth_ = fixed_depth_arg; + + if (!this->octree_) + { + return; + } + + // If I'm nowhere, reset + // If I'm somewhere and I can't guarantee I'm before the first node, reset + if ((!this->current_state_) || (fixed_depth_ <= this->getCurrentOctreeDepth ())) + OctreeBreadthFirstIterator::reset (); + + if (this->octree_->getTreeDepth () < fixed_depth_) + { + PCL_WARN ("[pcl::octree::FixedDepthIterator] The requested fixed depth was bigger than the octree's depth.\n"); + PCL_WARN ("[pcl::octree::FixedDepthIterator] fixed_depth = %d (instead of %d)\n", this->octree_->getTreeDepth (), fixed_depth_); + } + + // By default for the parent class OctreeBreadthFirstIterator, if the + // depth argument is equal to 0, the iterator would run over every node. + // For the OctreeFixedDepthIterator, whatever the depth ask, set the + // max_octree_depth_ accordingly + this->max_octree_depth_ = std::min (fixed_depth_, this->octree_->getTreeDepth ()); + + // Restore previous state in case breath first iterator had child nodes already set up + if (FIFO_.size ()) + this->current_state_ = &FIFO_.front (); + + // Iterate all the way to the desired level + while (this->current_state_ && (this->getCurrentOctreeDepth () != fixed_depth_)) + OctreeBreadthFirstIterator::operator++ (); + } + + ////////////////////////////////////////////////////////////////////////////////////////////// + template + OctreeLeafNodeBreadthFirstIterator::OctreeLeafNodeBreadthFirstIterator (unsigned int max_depth_arg) : + OctreeBreadthFirstIterator (max_depth_arg) + { + reset (); + } + + ////////////////////////////////////////////////////////////////////////////////////////////// + template + OctreeLeafNodeBreadthFirstIterator::OctreeLeafNodeBreadthFirstIterator (OctreeT* octree_arg, unsigned int max_depth_arg) : + OctreeBreadthFirstIterator (octree_arg, max_depth_arg) + { + reset (); + } + + ////////////////////////////////////////////////////////////////////////////////////////////// + template + OctreeLeafNodeBreadthFirstIterator::OctreeLeafNodeBreadthFirstIterator (OctreeT* octree_arg, + unsigned int max_depth_arg, + IteratorState* current_state, + const std::deque& fifo) + : OctreeBreadthFirstIterator (octree_arg, + max_depth_arg, + current_state, + fifo) + {} + + ////////////////////////////////////////////////////////////////////////////////////////////// + template + void OctreeLeafNodeBreadthFirstIterator::reset () + { + OctreeBreadthFirstIterator::reset (); + ++*this; + } + + ////////////////////////////////////////////////////////////////////////////////////////////// + template + OctreeLeafNodeBreadthFirstIterator& + OctreeLeafNodeBreadthFirstIterator::operator++ () + { + do + { + OctreeBreadthFirstIterator::operator++ (); + } while ((this->current_state_) && (this->current_state_->node_->getNodeType () != LEAF_NODE)); + + return (*this); + } + + ////////////////////////////////////////////////////////////////////////////////////////////// + template + OctreeLeafNodeBreadthFirstIterator + OctreeLeafNodeBreadthFirstIterator::operator++ (int) + { + OctreeLeafNodeBreadthFirstIterator _Tmp = *this; + ++*this; + return (_Tmp); + } } } diff --git a/octree/include/pcl/octree/impl/octree_pointcloud.hpp b/octree/include/pcl/octree/impl/octree_pointcloud.hpp index 267b25da..9bef2c48 100644 --- a/octree/include/pcl/octree/impl/octree_pointcloud.hpp +++ b/octree/include/pcl/octree/impl/octree_pointcloud.hpp @@ -129,13 +129,18 @@ pcl::octree::OctreePointCloud template bool pcl::octree::OctreePointCloud::isVoxelOccupiedAtPoint (const PointT& point_arg) const { + if (!isPointWithinBoundingBox (point_arg)) + { + return false; + } + OctreeKey key; // generate key for point this->genOctreeKeyforPoint (point_arg, key); // search for key in octree - return (isPointWithinBoundingBox (point_arg) && this->existLeaf (key)); + return (this->existLeaf (key)); } ////////////////////////////////////////////////////////////////////////////////////////////// @@ -154,18 +159,25 @@ template::isVoxelOccupiedAtPoint ( const double point_x_arg, const double point_y_arg, const double point_z_arg) const { - OctreeKey key; - - // generate key for point - this->genOctreeKeyforPoint (point_x_arg, point_y_arg, point_z_arg, key); + // create a new point with the argument coordinates + PointT point; + point.x = point_x_arg; + point.y = point_y_arg; + point.z = point_z_arg; - return (this->existLeaf (key)); + // search for voxel at point in octree + return (this->isVoxelOccupiedAtPoint (point)); } ////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::octree::OctreePointCloud::deleteVoxelAtPoint (const PointT& point_arg) { + if (!isPointWithinBoundingBox (point_arg)) + { + return; + } + OctreeKey key; // generate key for point diff --git a/octree/include/pcl/octree/impl/octree_pointcloud_adjacency.hpp b/octree/include/pcl/octree/impl/octree_pointcloud_adjacency.hpp index a3c5365a..20cdeb61 100644 --- a/octree/include/pcl/octree/impl/octree_pointcloud_adjacency.hpp +++ b/octree/include/pcl/octree/impl/octree_pointcloud_adjacency.hpp @@ -91,9 +91,9 @@ pcl::octree::OctreePointCloudAdjacency OctreePointCloud::addPointsFromInputCloud (); LeafContainerT *leaf_container; - typename OctreeAdjacencyT::LeafNodeIterator leaf_itr; + typename OctreeAdjacencyT::LeafNodeDepthFirstIterator leaf_itr; leaf_vector_.reserve (this->getLeafCount ()); - for ( leaf_itr = this->leaf_begin () ; leaf_itr != this->leaf_end (); ++leaf_itr) + for ( leaf_itr = this->leaf_depth_begin () ; leaf_itr != this->leaf_depth_end (); ++leaf_itr) { OctreeKey leaf_key = leaf_itr.getCurrentOctreeKey (); leaf_container = &(leaf_itr.getLeafContainer ()); @@ -221,7 +221,7 @@ pcl::octree::OctreePointCloudAdjacency voxel_adjacency_graph.clear (); //Add a vertex for each voxel, store ids in map std::map leaf_vertex_id_map; - for (typename OctreeAdjacencyT::LeafNodeIterator leaf_itr = this->leaf_begin () ; leaf_itr != this->leaf_end (); ++leaf_itr) + for (typename OctreeAdjacencyT::LeafNodeDepthFirstIterator leaf_itr = this->leaf_depth_begin () ; leaf_itr != this->leaf_depth_end (); ++leaf_itr) { OctreeKey leaf_key = leaf_itr.getCurrentOctreeKey (); PointT centroid_point; diff --git a/octree/include/pcl/octree/impl/octree_search.hpp b/octree/include/pcl/octree/impl/octree_search.hpp index 9dc682e5..9d2bff27 100644 --- a/octree/include/pcl/octree/impl/octree_search.hpp +++ b/octree/include/pcl/octree/impl/octree_search.hpp @@ -97,7 +97,7 @@ pcl::octree::OctreePointCloudSearch::n OctreeKey key; key.x = key.y = key.z = 0; - // initalize smallest point distance in search with high value + // initialize smallest point distance in search with high value double smallest_dist = std::numeric_limits::max (); getKNearestNeighborRecursive (p_q, k, this->root_node_, key, 1, smallest_dist, point_candidates); diff --git a/octree/include/pcl/octree/octree2buf_base.h b/octree/include/pcl/octree/octree2buf_base.h index d9a2dcd8..15b678de 100644 --- a/octree/include/pcl/octree/octree2buf_base.h +++ b/octree/include/pcl/octree/octree2buf_base.h @@ -233,7 +233,8 @@ namespace pcl friend class OctreeIteratorBase ; friend class OctreeDepthFirstIterator ; friend class OctreeBreadthFirstIterator ; - friend class OctreeLeafNodeIterator ; + friend class OctreeLeafNodeDepthFirstIterator ; + friend class OctreeLeafNodeBreadthFirstIterator ; typedef BufferedBranchNode BranchNode; typedef OctreeLeafNode LeafNode; @@ -248,10 +249,36 @@ namespace pcl const Iterator end() {return Iterator();}; // Octree leaf node iterators - typedef OctreeLeafNodeIterator LeafNodeIterator; - typedef const OctreeLeafNodeIterator ConstLeafNodeIterator; - LeafNodeIterator leaf_begin(unsigned int max_depth_arg = 0) {return LeafNodeIterator(this, max_depth_arg);}; - const LeafNodeIterator leaf_end() {return LeafNodeIterator();}; + // The previous deprecated names + // LeafNodeIterator and ConstLeafNodeIterator are deprecated. + // Please use LeafNodeDepthFirstIterator and ConstLeafNodeDepthFirstIterator instead. + typedef OctreeLeafNodeDepthFirstIterator LeafNodeIterator; + typedef const OctreeLeafNodeDepthFirstIterator ConstLeafNodeIterator; + + PCL_DEPRECATED ("Please use leaf_depth_begin () instead.") + LeafNodeIterator leaf_begin (unsigned int max_depth_arg = 0) + { + return LeafNodeIterator (this, max_depth_arg); + }; + + PCL_DEPRECATED ("Please use leaf_depth_end () instead.") + const LeafNodeIterator leaf_end () + { + return LeafNodeIterator (); + }; + + // The currently valide names + typedef OctreeLeafNodeDepthFirstIterator LeafNodeDepthFirstIterator; + typedef const OctreeLeafNodeDepthFirstIterator ConstLeafNodeDepthFirstIterator; + LeafNodeDepthFirstIterator leaf_depth_begin (unsigned int max_depth_arg = 0) + { + return LeafNodeDepthFirstIterator (this, max_depth_arg); + }; + + const LeafNodeDepthFirstIterator leaf_depth_end () + { + return LeafNodeDepthFirstIterator(); + }; // Octree depth-first iterators typedef OctreeDepthFirstIterator DepthFirstIterator; @@ -265,6 +292,20 @@ namespace pcl BreadthFirstIterator breadth_begin(unsigned int max_depth_arg = 0) {return BreadthFirstIterator(this, max_depth_arg);}; const BreadthFirstIterator breadth_end() {return BreadthFirstIterator();}; + // Octree leaf node iterators + typedef OctreeLeafNodeBreadthFirstIterator LeafNodeBreadthIterator; + typedef const OctreeLeafNodeBreadthFirstIterator ConstLeafNodeBreadthIterator; + + LeafNodeBreadthIterator leaf_breadth_begin (unsigned int max_depth_arg = 0u) + { + return LeafNodeBreadthIterator (this, max_depth_arg? max_depth_arg : this->octree_depth_); + }; + + const LeafNodeBreadthIterator leaf_breadth_end () + { + return LeafNodeBreadthIterator (this, 0, NULL); + }; + /** \brief Empty constructor. */ Octree2BufBase (); @@ -489,7 +530,7 @@ namespace pcl return ret; } - /** \brief Check for leaf not existance in the octree + /** \brief Check if leaf doesn't exist in the octree * \param key_arg: octree key addressing a leaf node. * \return "true" if leaf node is found; "false" otherwise * */ @@ -804,8 +845,8 @@ namespace pcl * \param key_arg: reference to an octree key * \param binary_tree_in_it_arg iterator of binary input data * \param binary_tree_in_it_end_arg - * \param leaf_container_vector_it_arg: iterator pointing to leaf containter pointers to be added to a leaf node - * \param leaf_container_vector_it_end_arg: iterator pointing to leaf containter pointers pointing to last object in input container. + * \param leaf_container_vector_it_arg: iterator pointing to leaf container pointers to be added to a leaf node + * \param leaf_container_vector_it_end_arg: iterator pointing to leaf container pointers pointing to last object in input container. * \param branch_reset_arg: Reset pointer array of current branch * \param do_XOR_decoding_arg: select if binary tree structure is based on current octree (false) of based on a XOR comparison between current and previous octree **/ diff --git a/octree/include/pcl/octree/octree_base.h b/octree/include/pcl/octree/octree_base.h index e004b67a..0a4b2133 100644 --- a/octree/include/pcl/octree/octree_base.h +++ b/octree/include/pcl/octree/octree_base.h @@ -61,7 +61,6 @@ namespace pcl typename BranchContainerT = OctreeContainerEmpty > class OctreeBase { - public: typedef OctreeBase OctreeT; @@ -72,36 +71,145 @@ namespace pcl typedef BranchContainerT BranchContainer; typedef LeafContainerT LeafContainer; + protected: + + /////////////////////////////////////////////////////////////////////// + // Members + /////////////////////////////////////////////////////////////////////// + + /** \brief Amount of leaf nodes **/ + std::size_t leaf_count_; + + /** \brief Amount of branch nodes **/ + std::size_t branch_count_; + + /** \brief Pointer to root branch node of octree **/ + BranchNode* root_node_; + + /** \brief Depth mask based on octree depth **/ + unsigned int depth_mask_; + + /** \brief Octree depth */ + unsigned int octree_depth_; + + /** \brief Enable dynamic_depth **/ + bool dynamic_depth_enabled_; + + /** \brief key range */ + OctreeKey max_key_; + + public: + // iterators are friends friend class OctreeIteratorBase ; friend class OctreeDepthFirstIterator ; friend class OctreeBreadthFirstIterator ; - friend class OctreeLeafNodeIterator ; + friend class OctreeFixedDepthIterator ; + friend class OctreeLeafNodeDepthFirstIterator ; + friend class OctreeLeafNodeBreadthFirstIterator ; // Octree default iterators typedef OctreeDepthFirstIterator Iterator; typedef const OctreeDepthFirstIterator ConstIterator; - Iterator begin(unsigned int max_depth_arg = 0) {return Iterator(this, max_depth_arg);}; - const Iterator end() {return Iterator();}; + + Iterator begin (unsigned int max_depth_arg = 0u) + { + return Iterator (this, max_depth_arg? max_depth_arg : this->octree_depth_); + }; + + const Iterator end () + { + return Iterator (this, 0, NULL); + }; // Octree leaf node iterators - typedef OctreeLeafNodeIterator LeafNodeIterator; - typedef const OctreeLeafNodeIterator ConstLeafNodeIterator; - LeafNodeIterator leaf_begin(unsigned int max_depth_arg = 0) {return LeafNodeIterator(this, max_depth_arg);}; - const LeafNodeIterator leaf_end() {return LeafNodeIterator();}; + // The previous deprecated names + // LeafNodeIterator and ConstLeafNodeIterator are deprecated. + // Please use LeafNodeDepthFirstIterator and ConstLeafNodeDepthFirstIterator instead. + typedef OctreeLeafNodeDepthFirstIterator LeafNodeIterator; + typedef const OctreeLeafNodeDepthFirstIterator ConstLeafNodeIterator; + + PCL_DEPRECATED ("Please use leaf_depth_begin () instead.") + LeafNodeIterator leaf_begin (unsigned int max_depth_arg = 0u) + { + return LeafNodeIterator (this, max_depth_arg? max_depth_arg : this->octree_depth_); + }; + + PCL_DEPRECATED ("Please use leaf_depth_end () instead.") + const LeafNodeIterator leaf_end () + { + return LeafNodeIterator (this, 0, NULL); + }; + + // The currently valide names + typedef OctreeLeafNodeDepthFirstIterator LeafNodeDepthFirstIterator; + typedef const OctreeLeafNodeDepthFirstIterator ConstLeafNodeDepthFirstIterator; + + LeafNodeDepthFirstIterator leaf_depth_begin (unsigned int max_depth_arg = 0u) + { + return LeafNodeDepthFirstIterator (this, max_depth_arg? max_depth_arg : this->octree_depth_); + }; + + const LeafNodeDepthFirstIterator leaf_depth_end () + { + return LeafNodeDepthFirstIterator (this, 0, NULL); + }; // Octree depth-first iterators typedef OctreeDepthFirstIterator DepthFirstIterator; typedef const OctreeDepthFirstIterator ConstDepthFirstIterator; - DepthFirstIterator depth_begin(unsigned int max_depth_arg = 0) {return DepthFirstIterator(this, max_depth_arg);}; - const DepthFirstIterator depth_end() {return DepthFirstIterator();}; + + DepthFirstIterator depth_begin (unsigned int max_depth_arg = 0u) + { + return DepthFirstIterator (this, max_depth_arg? max_depth_arg : this->octree_depth_); + }; + + const DepthFirstIterator depth_end () + { + return DepthFirstIterator (this, 0, NULL); + }; // Octree breadth-first iterators typedef OctreeBreadthFirstIterator BreadthFirstIterator; typedef const OctreeBreadthFirstIterator ConstBreadthFirstIterator; - BreadthFirstIterator breadth_begin(unsigned int max_depth_arg = 0) {return BreadthFirstIterator(this, max_depth_arg);}; - const BreadthFirstIterator breadth_end() {return BreadthFirstIterator();}; + BreadthFirstIterator breadth_begin (unsigned int max_depth_arg = 0u) + { + return BreadthFirstIterator (this, max_depth_arg? max_depth_arg : this->octree_depth_); + }; + + const BreadthFirstIterator breadth_end () + { + return BreadthFirstIterator (this, 0, NULL); + }; + + // Octree breadth iterators at a given depth + typedef OctreeFixedDepthIterator FixedDepthIterator; + typedef const OctreeFixedDepthIterator ConstFixedDepthIterator; + + FixedDepthIterator fixed_depth_begin (unsigned int fixed_depth_arg = 0u) + { + return FixedDepthIterator (this, fixed_depth_arg); + }; + + const FixedDepthIterator fixed_depth_end () + { + return FixedDepthIterator (this, 0, NULL); + }; + + // Octree leaf node iterators + typedef OctreeLeafNodeBreadthFirstIterator LeafNodeBreadthFirstIterator; + typedef const OctreeLeafNodeBreadthFirstIterator ConstLeafNodeBreadthFirstIterator; + + LeafNodeBreadthFirstIterator leaf_breadth_begin (unsigned int max_depth_arg = 0u) + { + return LeafNodeBreadthFirstIterator (this, max_depth_arg? max_depth_arg : this->octree_depth_); + }; + + const LeafNodeBreadthFirstIterator leaf_breadth_end () + { + return LeafNodeBreadthFirstIterator (this, 0, NULL); + }; /** \brief Empty constructor. */ OctreeBase (); @@ -284,7 +392,7 @@ namespace pcl return result; } - /** \brief Check for existance of a leaf node in the octree + /** \brief Check for existence of a leaf node in the octree * \param key_arg: octree key addressing a leaf node. * \return "true" if leaf node is found; "false" otherwise * */ @@ -564,31 +672,6 @@ namespace pcl { return (true); } - - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Globals - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - /** \brief Amount of leaf nodes **/ - std::size_t leaf_count_; - - /** \brief Amount of branch nodes **/ - std::size_t branch_count_; - - /** \brief Pointer to root branch node of octree **/ - BranchNode* root_node_; - - /** \brief Depth mask based on octree depth **/ - unsigned int depth_mask_; - - /** \brief Octree depth */ - unsigned int octree_depth_; - - /** \brief Enable dynamic_depth **/ - bool dynamic_depth_enabled_; - - /** \brief key range */ - OctreeKey max_key_; }; } } diff --git a/octree/include/pcl/octree/octree_iterator.h b/octree/include/pcl/octree/octree_iterator.h index edcbed5c..cef9a989 100644 --- a/octree/include/pcl/octree/octree_iterator.h +++ b/octree/include/pcl/octree/octree_iterator.h @@ -3,6 +3,7 @@ * * Point Cloud Library (PCL) - www.pointclouds.org * Copyright (c) 2010-2012, Willow Garage, Inc. + * Copyright (c) 2017-, Open Perception, Inc. * * All rights reserved. * @@ -62,7 +63,7 @@ namespace pcl struct IteratorState{ OctreeNode* node_; OctreeKey key_; - unsigned char depth_; + unsigned int depth_; }; @@ -103,26 +104,21 @@ namespace pcl this->reset (); } - /** \brief Copy constructor. - * \param[in] src the iterator to copy into this - * \param[in] max_depth_arg Depth limitation during traversal - */ - OctreeIteratorBase (const OctreeIteratorBase& src, unsigned int max_depth_arg = 0) : - octree_ (src.octree_), current_state_(0), max_octree_depth_(max_depth_arg) - { - } - - /** \brief Copy operator. - * \param[in] src the iterator to copy into this - */ - inline OctreeIteratorBase& - operator = (const OctreeIteratorBase& src) - { - octree_ = src.octree_; - current_state_ = src.current_state_; - max_octree_depth_ = src.max_octree_depth_; - return (*this); - } + /** \brief Constructor. + * \param[in] octree_arg Octree to be iterated. Initially the iterator is set to its root node. + * \param[in] max_depth_arg Depth limitation during traversal + * \param[in] current_state A pointer to the current iterator state + * + * \warning For advanced users only. + */ + explicit + OctreeIteratorBase (OctreeT* octree_arg, + unsigned int max_depth_arg, + IteratorState* current_state) + : octree_(octree_arg) + , current_state_ (current_state) + , max_octree_depth_ (max_depth_arg) + {} /** \brief Empty deconstructor. */ virtual @@ -135,9 +131,17 @@ namespace pcl */ bool operator==(const OctreeIteratorBase& other) const { - return (( octree_ ==other.octree_) && - ( current_state_ == other.current_state_) && - ( max_octree_depth_ == other.max_octree_depth_) ); + if (this == &other) // same object + return true; + if (octree_ != other.octree_) // refer to different octrees + return false; + if (!current_state_ && !other.current_state_) // both are end iterators + return true; + if (max_octree_depth_ == other.max_octree_depth_ && + current_state_ && other.current_state_ && // null dereference protection + current_state_->key_ == other.current_state_->key_) + return true; + return false; } /** \brief Inequal comparison operator @@ -145,9 +149,7 @@ namespace pcl */ bool operator!=(const OctreeIteratorBase& other) const { - return (( octree_ !=other.octree_) && - ( current_state_ != other.current_state_) && - ( max_octree_depth_ != other.max_octree_depth_) ); + return !operator== (other); } /** \brief Reset iterator */ @@ -384,11 +386,33 @@ namespace pcl explicit OctreeDepthFirstIterator (OctreeT* octree_arg, unsigned int max_depth_arg = 0); - /** \brief Empty deconstructor. */ - virtual - ~OctreeDepthFirstIterator (); + /** \brief Constructor. + * \param[in] octree_arg Octree to be iterated. Initially the iterator is set to its root node. + * \param[in] max_depth_arg Depth limitation during traversal + * \param[in] current_state A pointer to the current iterator state + * + * \warning For advanced users only. + */ + explicit + OctreeDepthFirstIterator (OctreeT* octree_arg, + unsigned int max_depth_arg, + IteratorState* current_state, + const std::vector& stack = std::vector ()) + : OctreeIteratorBase (octree_arg, max_depth_arg, current_state) + , stack_ (stack) + {} + + /** \brief Copy Constructor. + * \param[in] other Another OctreeDepthFirstIterator to copy from + */ + OctreeDepthFirstIterator (const OctreeDepthFirstIterator& other) + : OctreeIteratorBase (other) + , stack_ (other.stack_) + { + this->current_state_ = stack_.size ()? &stack_.back () : NULL; + } - /** \brief Copy operator. + /** \brief Copy assignment * \param[in] src the iterator to copy into this */ inline OctreeDepthFirstIterator& @@ -401,7 +425,7 @@ namespace pcl if (stack_.size()) { - this->current_state_ = &stack_.back(); + this->current_state_ = &stack_.back (); } else { this->current_state_ = 0; @@ -451,12 +475,11 @@ namespace pcl template class OctreeBreadthFirstIterator : public OctreeIteratorBase { + public: // public typedefs typedef typename OctreeIteratorBase::BranchNode BranchNode; typedef typename OctreeIteratorBase::LeafNode LeafNode; - - public: /** \brief Empty constructor. * \param[in] max_depth_arg Depth limitation during traversal */ @@ -470,9 +493,31 @@ namespace pcl explicit OctreeBreadthFirstIterator (OctreeT* octree_arg, unsigned int max_depth_arg = 0); - /** \brief Empty deconstructor. */ - virtual - ~OctreeBreadthFirstIterator (); + /** \brief Constructor. + * \param[in] octree_arg Octree to be iterated. Initially the iterator is set to its root node. + * \param[in] max_depth_arg Depth limitation during traversal + * \param[in] current_state A pointer to the current iterator state + * + * \warning For advanced users only. + */ + explicit + OctreeBreadthFirstIterator (OctreeT* octree_arg, + unsigned int max_depth_arg, + IteratorState* current_state, + const std::deque& fifo = std::deque ()) + : OctreeIteratorBase (octree_arg, max_depth_arg, current_state) + , FIFO_ (fifo) + {} + + /** \brief Copy Constructor. + * \param[in] other Another OctreeBreadthFirstIterator to copy from + */ + OctreeBreadthFirstIterator (const OctreeBreadthFirstIterator& other) + : OctreeIteratorBase (other) + , FIFO_ (other.FIFO_) + { + this->current_state_ = FIFO_.size ()? &FIFO_.front () : NULL; + } /** \brief Copy operator. * \param[in] src the iterator to copy into this @@ -523,6 +568,92 @@ namespace pcl std::deque FIFO_; }; + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** \brief @b Octree iterator class + * \note Iterator over all existing nodes at a given depth. It walks across an octree + * in a breadth-first manner. + * \ingroup octree + * \author Fabien Rozar (fabien.rozar@gmail.com) + */ + template + class OctreeFixedDepthIterator : public OctreeBreadthFirstIterator + { + public: + + // public typedefs + using typename OctreeBreadthFirstIterator::BranchNode; + using typename OctreeBreadthFirstIterator::LeafNode; + + /** \brief Empty constructor. + */ + OctreeFixedDepthIterator (); + + /** \brief Constructor. + * \param[in] octree_arg Octree to be iterated. Initially the iterator is set to its root node. + * \param[in] fixed_depth_arg Depth level during traversal + */ + explicit + OctreeFixedDepthIterator (OctreeT* octree_arg, unsigned int fixed_depth_arg = 0); + + /** \brief Constructor. + * \param[in] octree_arg Octree to be iterated. Initially the iterator is set to its root node. + * \param[in] fixed_depth_arg Depth level during traversal + * \param[in] current_state A pointer to the current iterator state + * \param[in] fifo Internal container of octree node to go through + * + * \warning For advanced users only. + */ + OctreeFixedDepthIterator (OctreeT* octree_arg, + unsigned int fixed_depth_arg, + IteratorState* current_state, + const std::deque& fifo = std::deque ()) + : OctreeBreadthFirstIterator (octree_arg, fixed_depth_arg, current_state, fifo) + , fixed_depth_ (fixed_depth_arg) + {} + + /** \brief Copy Constructor. + * \param[in] other Another OctreeFixedDepthIterator to copy from + */ + OctreeFixedDepthIterator (const OctreeFixedDepthIterator& other) + : OctreeBreadthFirstIterator (other) + { + this->fixed_depth_ = other.fixed_depth_; + } + + /** \brief Copy assignment. + * \param[in] src the iterator to copy into this + * \return pointer to the current octree node + */ + inline OctreeFixedDepthIterator& + operator = (const OctreeFixedDepthIterator& src) + { + OctreeBreadthFirstIterator::operator= (src); + this->fixed_depth_ = src.fixed_depth_; + + return (*this); + } + + /** \brief Reset the iterator to the first node at the depth given as parameter + * \param[in] fixed_depth_arg Depth level during traversal + */ + void + reset (unsigned int fixed_depth_arg); + + /** \brief Reset the iterator to the first node at the current depth + */ + void + reset () + { + this->reset (fixed_depth_); + } + + protected: + using OctreeBreadthFirstIterator::FIFO_; + + /** \brief Given level of the node to be iterated */ + unsigned int fixed_depth_; + }; + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** \brief Octree leaf node iterator class * \note This class implements a forward iterator for traversing the leaf nodes of an octree data structure. @@ -531,7 +662,7 @@ namespace pcl */ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template - class OctreeLeafNodeIterator : public OctreeDepthFirstIterator + class OctreeLeafNodeDepthFirstIterator : public OctreeDepthFirstIterator { typedef typename OctreeDepthFirstIterator::BranchNode BranchNode; typedef typename OctreeDepthFirstIterator::LeafNode LeafNode; @@ -541,7 +672,7 @@ namespace pcl * \param[in] max_depth_arg Depth limitation during traversal */ explicit - OctreeLeafNodeIterator (unsigned int max_depth_arg = 0) : + OctreeLeafNodeDepthFirstIterator (unsigned int max_depth_arg = 0) : OctreeDepthFirstIterator (max_depth_arg) { reset (); @@ -552,17 +683,29 @@ namespace pcl * \param[in] max_depth_arg Depth limitation during traversal */ explicit - OctreeLeafNodeIterator (OctreeT* octree_arg, unsigned int max_depth_arg = 0) : + OctreeLeafNodeDepthFirstIterator (OctreeT* octree_arg, unsigned int max_depth_arg = 0) : OctreeDepthFirstIterator (octree_arg, max_depth_arg) { reset (); } - /** \brief Empty deconstructor. */ - virtual - ~OctreeLeafNodeIterator () - { - } + /** \brief Constructor. + * \param[in] octree_arg Octree to be iterated. Initially the iterator is set to its root node. + * \param[in] max_depth_arg Depth limitation during traversal + * \param[in] current_state A pointer to the current iterator state + * + * \warning For advanced users only. + */ + explicit + OctreeLeafNodeDepthFirstIterator (OctreeT* octree_arg, + unsigned int max_depth_arg, + IteratorState* current_state, + const std::vector& stack = std::vector ()) + : OctreeDepthFirstIterator (octree_arg, + max_depth_arg, + current_state, + stack) + {} /** \brief Reset the iterator to the root node of the octree */ @@ -576,7 +719,7 @@ namespace pcl /** \brief Preincrement operator. * \note recursively step to next octree leaf node */ - inline OctreeLeafNodeIterator& + inline OctreeLeafNodeDepthFirstIterator& operator++ () { do @@ -590,10 +733,10 @@ namespace pcl /** \brief postincrement operator. * \note step to next octree node */ - inline OctreeLeafNodeIterator + inline OctreeLeafNodeDepthFirstIterator operator++ (int) { - OctreeLeafNodeIterator _Tmp = *this; + OctreeLeafNodeDepthFirstIterator _Tmp = *this; ++*this; return (_Tmp); } @@ -613,6 +756,67 @@ namespace pcl } }; + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** \brief Octree leaf node iterator class + * \note This class implements a forward iterator for traversing the leaf nodes of an octree data structure + * in the breadth first way. + * \ingroup octree + * \author Fabien Rozar (fabien.rozar@gmail.com) + */ + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + template + class OctreeLeafNodeBreadthFirstIterator : public OctreeBreadthFirstIterator + { + typedef typename OctreeBreadthFirstIterator::BranchNode BranchNode; + typedef typename OctreeBreadthFirstIterator::LeafNode LeafNode; + + public: + /** \brief Empty constructor. + * \param[in] max_depth_arg Depth limitation during traversal + */ + explicit + OctreeLeafNodeBreadthFirstIterator (unsigned int max_depth_arg = 0); + + /** \brief Constructor. + * \param[in] octree_arg Octree to be iterated. Initially the iterator is set to its root node. + * \param[in] max_depth_arg Depth limitation during traversal + */ + explicit + OctreeLeafNodeBreadthFirstIterator (OctreeT* octree_arg, unsigned int max_depth_arg = 0); + + /** \brief Copy constructor. + * \param[in] octree_arg Octree to be iterated. Initially the iterator is set to its root node. + * \param[in] max_depth_arg Depth limitation during traversal + * \param[in] current_state A pointer to the current iterator state + * \param[in] fifo Internal container of octree node to go through + * + * \warning For advanced users only. + */ + explicit + OctreeLeafNodeBreadthFirstIterator (OctreeT* octree_arg, + unsigned int max_depth_arg, + IteratorState* current_state, + const std::deque& fifo = std::deque ()); + + /** \brief Reset the iterator to the first leaf in the breadth first way. + */ + inline void + reset (); + + /** \brief Preincrement operator. + * \note recursively step to next octree leaf node + */ + inline OctreeLeafNodeBreadthFirstIterator& + operator++ (); + + + /** \brief Postincrement operator. + * \note step to next octree node + */ + inline OctreeLeafNodeBreadthFirstIterator + operator++ (int); + }; + } } diff --git a/octree/include/pcl/octree/octree_key.h b/octree/include/pcl/octree/octree_key.h index 4442cef4..dbde9821 100644 --- a/octree/include/pcl/octree/octree_key.h +++ b/octree/include/pcl/octree/octree_key.h @@ -79,6 +79,15 @@ namespace pcl return ((b.x == this->x) && (b.y == this->y) && (b.z == this->z)); } + /** \brief Inequal comparison operator + * \param[in] other OctreeIteratorBase to compare with + * \return "true" if the current and other iterators are different ; "false" otherwise. + */ + bool operator!= (const OctreeKey& other) const + { + return !operator== (other); + } + /** \brief Operator<= for comparing octree keys with each other. * \return "true" if key indices are not greater than the key indices of b ; "false" otherwise. * */ @@ -131,7 +140,7 @@ namespace pcl } /* \brief maximum depth that can be addressed */ - static const unsigned char maxDepth = static_cast(sizeof(uint32_t)*8); + static const unsigned char maxDepth = static_cast(sizeof(uint32_t)*8); // Indices addressing a voxel at (X, Y, Z) diff --git a/octree/include/pcl/octree/octree_pointcloud.h b/octree/include/pcl/octree/octree_pointcloud.h index 73cf607c..ba9e37d0 100644 --- a/octree/include/pcl/octree/octree_pointcloud.h +++ b/octree/include/pcl/octree/octree_pointcloud.h @@ -72,34 +72,12 @@ namespace pcl class OctreePointCloud : public OctreeT { - // iterators are friends - friend class OctreeIteratorBase ; - friend class OctreeDepthFirstIterator ; - friend class OctreeBreadthFirstIterator ; - friend class OctreeLeafNodeIterator ; - public: typedef OctreeT Base; typedef typename OctreeT::LeafNode LeafNode; typedef typename OctreeT::BranchNode BranchNode; - // Octree default iterators - typedef OctreeDepthFirstIterator Iterator; - typedef const OctreeDepthFirstIterator ConstIterator; - - // Octree leaf node iterators - typedef OctreeLeafNodeIterator LeafNodeIterator; - typedef const OctreeLeafNodeIterator ConstLeafNodeIterator; - - // Octree depth-first iterators - typedef OctreeDepthFirstIterator DepthFirstIterator; - typedef const OctreeDepthFirstIterator ConstDepthFirstIterator; - - // Octree breadth-first iterators - typedef OctreeBreadthFirstIterator BreadthFirstIterator; - typedef const OctreeBreadthFirstIterator ConstBreadthFirstIterator; - /** \brief Octree pointcloud constructor. * \param[in] resolution_arg octree resolution at lowest octree level */ diff --git a/octree/include/pcl/octree/octree_pointcloud_adjacency.h b/octree/include/pcl/octree/octree_pointcloud_adjacency.h index d213a8c3..d7a894fe 100644 --- a/octree/include/pcl/octree/octree_pointcloud_adjacency.h +++ b/octree/include/pcl/octree/octree_pointcloud_adjacency.h @@ -95,26 +95,6 @@ namespace pcl typedef boost::shared_ptr PointCloudPtr; typedef boost::shared_ptr PointCloudConstPtr; - // Iterators are friends - friend class OctreeIteratorBase; - friend class OctreeDepthFirstIterator; - friend class OctreeBreadthFirstIterator; - friend class OctreeLeafNodeIterator; - - // Octree default iterators - typedef OctreeDepthFirstIterator Iterator; - typedef const OctreeDepthFirstIterator ConstIterator; - - Iterator depth_begin (unsigned int max_depth_arg = 0) { return Iterator (this, max_depth_arg); } - const Iterator depth_end () { return Iterator (); } - - // Octree leaf node iterators - typedef OctreeLeafNodeIterator LeafNodeIterator; - typedef const OctreeLeafNodeIterator ConstLeafNodeIterator; - - LeafNodeIterator leaf_begin (unsigned int max_depth_arg = 0) { return LeafNodeIterator (this, max_depth_arg); } - const LeafNodeIterator leaf_end () { return LeafNodeIterator (); } - // BGL graph typedef boost::adjacency_list VoxelAdjacencyList; typedef typename VoxelAdjacencyList::vertex_descriptor VoxelID; diff --git a/octree/include/pcl/octree/octree_search.h b/octree/include/pcl/octree/octree_search.h index 08e99793..165f7faf 100644 --- a/octree/include/pcl/octree/octree_search.h +++ b/octree/include/pcl/octree/octree_search.h @@ -242,6 +242,7 @@ namespace pcl /** \brief Search for points within rectangular search area + * Points exactly on the edges of the search rectangle are included. * \param[in] min_pt lower corner of search area * \param[in] max_pt upper corner of search area * \param[out] k_indices the resultant point indices diff --git a/octree/octree.doxy b/octree/octree.doxy index d05db86a..a68de546 100644 --- a/octree/octree.doxy +++ b/octree/octree.doxy @@ -21,7 +21,7 @@ deallocation operations in scenarios where octrees needs to be created at high r The following figure illustrates the voxel bounding boxes of an octree nodes at lowest tree level. The octree voxels are surrounding every 3D point from the bunny's surface. The red dots represent the point data. -This image is create with the octree_viewer (visualization/tools/octree_viewer). +This image is created with the octree_viewer (visualization/tools/octree_viewer). For examples how to use the pcl_octree library, please visit the pcl tutorial page. diff --git a/outofcore/include/pcl/outofcore/cJSON.h b/outofcore/include/pcl/outofcore/cJSON.h index 89f6a08f..29f9c938 100644 --- a/outofcore/include/pcl/outofcore/cJSON.h +++ b/outofcore/include/pcl/outofcore/cJSON.h @@ -113,7 +113,7 @@ PCLAPI(void) cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) PCLAPI(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); PCLAPI(void) cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); -/* Remove/Detatch items from Arrays/Objects. */ +/* Remove/Detach items from Arrays/Objects. */ PCLAPI(cJSON *) cJSON_DetachItemFromArray(cJSON *array,int which); PCLAPI(void) cJSON_DeleteItemFromArray(cJSON *array,int which); PCLAPI(cJSON *) cJSON_DetachItemFromObject(cJSON *object,const char *string); diff --git a/outofcore/include/pcl/outofcore/impl/octree_base_node.hpp b/outofcore/include/pcl/outofcore/impl/octree_base_node.hpp index 6da81ec2..7bd589f6 100644 --- a/outofcore/include/pcl/outofcore/impl/octree_base_node.hpp +++ b/outofcore/include/pcl/outofcore/impl/octree_base_node.hpp @@ -1622,7 +1622,7 @@ namespace pcl if (num_pts > 0) { //always sample at least one point - sample_points = sample_points > 0 ? sample_points : 1; + sample_points = sample_points > 1 ? sample_points : 1; } else { @@ -1639,6 +1639,7 @@ namespace pcl random_sampler.setSample (static_cast (sample_points)); pcl::ExtractIndices extractor; + extractor.setInputCloud (tmp_blob); pcl::IndicesPtr downsampled_cloud_indices (new std::vector ()); random_sampler.filter (*downsampled_cloud_indices); diff --git a/outofcore/include/pcl/outofcore/impl/octree_disk_container.hpp b/outofcore/include/pcl/outofcore/impl/octree_disk_container.hpp index bd4fe59a..2cda7afa 100644 --- a/outofcore/include/pcl/outofcore/impl/octree_disk_container.hpp +++ b/outofcore/include/pcl/outofcore/impl/octree_disk_container.hpp @@ -74,7 +74,7 @@ namespace pcl OutofcoreOctreeDiskContainer::rand_gen_ (static_cast (std::time(NULL))); template - boost::uuids::random_generator OutofcoreOctreeDiskContainer::uuid_gen_ (&rand_gen_); + boost::uuids::basic_random_generator OutofcoreOctreeDiskContainer::uuid_gen_ (&rand_gen_); template const uint64_t OutofcoreOctreeDiskContainer::READ_BLOCK_SIZE_ = static_cast (2e12); @@ -245,7 +245,7 @@ namespace pcl if ((start + count) > size ()) { - PCL_ERROR ("[pcl::outofcore::OutofcoreOctreeDiskContainer::%s] Indicies out of range; start + count exceeds the size of the stored points\n", __FUNCTION__); + PCL_ERROR ("[pcl::outofcore::OutofcoreOctreeDiskContainer::%s] Indices out of range; start + count exceeds the size of the stored points\n", __FUNCTION__); PCL_THROW_EXCEPTION (PCLException, "[pcl::outofcore::OutofcoreOctreeDiskContainer] Outofcore Octree Exception: Read indices exceed range"); } diff --git a/outofcore/include/pcl/outofcore/octree_base_node.h b/outofcore/include/pcl/outofcore/octree_base_node.h index fff47002..a39202ac 100644 --- a/outofcore/include/pcl/outofcore/octree_base_node.h +++ b/outofcore/include/pcl/outofcore/octree_base_node.h @@ -328,7 +328,7 @@ namespace pcl * * \param bb_min triple of x,y,z minima for bounding box * \param bb_max triple of x,y,z maxima for bounding box - * \param tree adress of the tree data structure that will hold this initial root node + * \param tree address of the tree data structure that will hold this initial root node * \param rootname Root directory for location of on-disk octree storage; if directory * doesn't exist, it is created; if "rootname" is an existing file, * diff --git a/outofcore/include/pcl/outofcore/octree_disk_container.h b/outofcore/include/pcl/outofcore/octree_disk_container.h index 9978b9ce..d10af697 100644 --- a/outofcore/include/pcl/outofcore/octree_disk_container.h +++ b/outofcore/include/pcl/outofcore/octree_disk_container.h @@ -258,7 +258,7 @@ namespace pcl /** \brief Generate a universally unique identifier (UUID) * - * A mutex lock happens to ensure uniquness + * A mutex lock happens to ensure uniqueness * */ static void @@ -296,7 +296,7 @@ namespace pcl static boost::mutex rng_mutex_; static boost::mt19937 rand_gen_; - static boost::uuids::random_generator uuid_gen_; + static boost::uuids::basic_random_generator uuid_gen_; }; } //namespace outofcore diff --git a/outofcore/include/pcl/outofcore/octree_ram_container.h b/outofcore/include/pcl/outofcore/octree_ram_container.h index d8988188..2ab72bfa 100644 --- a/outofcore/include/pcl/outofcore/octree_ram_container.h +++ b/outofcore/include/pcl/outofcore/octree_ram_container.h @@ -65,7 +65,7 @@ namespace pcl public: typedef typename OutofcoreAbstractNodeContainer::AlignedPointTVector AlignedPointTVector; - /** \brief empty contructor (with a path parameter?) + /** \brief empty constructor (with a path parameter?) */ OutofcoreOctreeRamContainer (const boost::filesystem::path&) : container_ () { } diff --git a/outofcore/src/outofcore_base_data.cpp b/outofcore/src/outofcore_base_data.cpp index 0b76e3e9..3fb8a236 100644 --- a/outofcore/src/outofcore_base_data.cpp +++ b/outofcore/src/outofcore_base_data.cpp @@ -137,6 +137,9 @@ namespace pcl void OutofcoreOctreeBaseMetadata::serializeMetadataToDisk () { + if (LOD_num_points_.empty ()) + return; + // Create JSON object boost::shared_ptr idx (cJSON_CreateObject (), cJSON_Delete); diff --git a/outofcore/tools/outofcore_print.cpp b/outofcore/tools/outofcore_print.cpp index 18e2910a..a8110fe5 100644 --- a/outofcore/tools/outofcore_print.cpp +++ b/outofcore/tools/outofcore_print.cpp @@ -237,7 +237,7 @@ outofcorePrint (boost::filesystem::path tree_root, size_t print_depth, bool boun void printHelp (int, char **argv) { - print_info ("This program is used to process pcd fiels into an outofcore data structure viewable by the"); + print_info ("This program is used to process pcd files into an outofcore data structure viewable by the"); print_info ("pcl_outofcore_viewer\n\n"); print_info ("%s \n", argv[0]); print_info ("\n"); diff --git a/outofcore/tools/outofcore_process.cpp b/outofcore/tools/outofcore_process.cpp index 78d2402e..bd175d31 100644 --- a/outofcore/tools/outofcore_process.cpp +++ b/outofcore/tools/outofcore_process.cpp @@ -229,7 +229,7 @@ outofcoreProcess (std::vector pcd_paths, boost::filesys void printHelp (int, char **argv) { - print_info ("This program is used to process pcd fiels into an outofcore data structure viewable by the"); + print_info ("This program is used to process pcd files into an outofcore data structure viewable by the"); print_info ("pcl_outofcore_viewer\n\n"); print_info ("%s .pcd \n", argv[0]); print_info ("\n"); diff --git a/pcl.png b/pcl.png new file mode 100644 index 0000000000000000000000000000000000000000..3e8b3130031b21f89a4712aa21d1c01ada3fe3bd GIT binary patch literal 14345 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::createSubClusters (pcl::people::Per Eigen::Vector3f p_current_eigen(current_point->x, current_point->y, current_point->z); // conversion to eigen float t = p_current_eigen.dot(head_ground_coeffs) / normalize_factor; // height from the ground maxima_projected.col(i).matrix () = p_current_eigen - head_ground_coeffs * t; // projection of the point on the groundplane - subclusters_number_of_points(i) = 0; // intialize number of points + subclusters_number_of_points(i) = 0; // initialize number of points } // Associate cluster points to one of the maximum: diff --git a/people/src/hog.cpp b/people/src/hog.cpp index 713f590f..4d4a926b 100644 --- a/people/src/hog.cpp +++ b/people/src/hog.cpp @@ -418,7 +418,8 @@ pcl::people::HOG::acosTable () const { int i, n=25000, n2=n/2; float t, ni; static float a[25000]; static bool init=false; - if( init ) return a+n2; ni = 2.02f/(float) n; + if( init ) return a+n2; + ni = 2.02f/(float) n; for( i=0; i1 ? 1 : t); diff --git a/recognition/include/pcl/recognition/color_gradient_modality.h b/recognition/include/pcl/recognition/color_gradient_modality.h index 295893be..566d4622 100644 --- a/recognition/include/pcl/recognition/color_gradient_modality.h +++ b/recognition/include/pcl/recognition/color_gradient_modality.h @@ -149,7 +149,7 @@ namespace pcl return (filtered_quantized_color_gradients_); } - /** \brief Returns a reference to the internally computed spreaded quantized map. */ + /** \brief Returns a reference to the internally computed spread quantized map. */ inline QuantizedMap & getSpreadedQuantizedMap () { @@ -243,7 +243,7 @@ namespace pcl /** \brief Determines whether variable numbers of features are extracted or not. */ bool variable_feature_nr_; - /** \brief Stores a smoothed verion of the input cloud. */ + /** \brief Stores a smoothed version of the input cloud. */ pcl::PointCloud::Ptr smoothed_input_; /** \brief Defines which feature selection method is used. */ @@ -264,7 +264,7 @@ namespace pcl pcl::QuantizedMap quantized_color_gradients_; /** \brief The map which holds the filtered quantized data. */ pcl::QuantizedMap filtered_quantized_color_gradients_; - /** \brief The map which holds the spreaded quantized data. */ + /** \brief The map which holds the spread quantized data. */ pcl::QuantizedMap spreaded_filtered_quantized_color_gradients_; }; diff --git a/recognition/include/pcl/recognition/crh_alignment.h b/recognition/include/pcl/recognition/crh_alignment.h index 0a2a4590..43b81ad8 100644 --- a/recognition/include/pcl/recognition/crh_alignment.h +++ b/recognition/include/pcl/recognition/crh_alignment.h @@ -176,7 +176,7 @@ namespace pcl } - /** \brief Computes the roll angle that aligns input to modle. + /** \brief Computes the roll angle that aligns input to model. * \param[in] input_ftt CRH histogram of the input cloud * \param[in] target_ftt CRH histogram of the target cloud * \param[out] peaks Vector containing angles where the histograms correlate diff --git a/recognition/include/pcl/recognition/face_detection/face_common.h b/recognition/include/pcl/recognition/face_detection/face_common.h index 8161f032..d2cdc0c6 100644 --- a/recognition/include/pcl/recognition/face_detection/face_common.h +++ b/recognition/include/pcl/recognition/face_detection/face_common.h @@ -2,6 +2,7 @@ #define FACE_DETECTOR_COMMON_H_ #include +#include namespace pcl { @@ -18,6 +19,7 @@ namespace pcl //save pose head information Eigen::Vector3f trans_; Eigen::Vector3f rot_; + EIGEN_MAKE_ALIGNED_OPERATOR_NEW }; class FeatureType @@ -83,6 +85,8 @@ namespace pcl Eigen::Matrix3d covariance_trans_; Eigen::Matrix3d covariance_rot_; + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + void serialize(::std::ostream & stream) const { diff --git a/recognition/include/pcl/recognition/hv/hv_go.h b/recognition/include/pcl/recognition/hv/hv_go.h index 557a26f6..36127754 100644 --- a/recognition/include/pcl/recognition/hv/hv_go.h +++ b/recognition/include/pcl/recognition/hv/hv_go.h @@ -94,7 +94,7 @@ namespace pcl { solution_[index] = val; //update optimizer solution - cost_ = opt_->evaluateSolution (solution_, index); //this will udpate the cost function in opt_ + cost_ = opt_->evaluateSolution (solution_, index); //this will update the cost function in opt_ } void setSolution(std::vector & sol) { @@ -447,6 +447,11 @@ namespace pcl void verify(); + + void setResolutionOccupancyGrid(float r) + { + res_occupancy_grid_ = r; + } void setRadiusNormals(float r) { diff --git a/recognition/include/pcl/recognition/impl/cg/geometric_consistency.hpp b/recognition/include/pcl/recognition/impl/cg/geometric_consistency.hpp index 71ef8408..d54595f3 100644 --- a/recognition/include/pcl/recognition/impl/cg/geometric_consistency.hpp +++ b/recognition/include/pcl/recognition/impl/cg/geometric_consistency.hpp @@ -46,10 +46,10 @@ #include ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool -gcCorrespSorter (pcl::Correspondence i, pcl::Correspondence j) -{ - return (i.distance < j.distance); +inline bool +gcCorrespSorter (pcl::Correspondence i, pcl::Correspondence j) +{ + return (i.distance < j.distance); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/recognition/include/pcl/recognition/impl/cg/hough_3d.hpp b/recognition/include/pcl/recognition/impl/cg/hough_3d.hpp index 030cae36..4065f627 100644 --- a/recognition/include/pcl/recognition/impl/cg/hough_3d.hpp +++ b/recognition/include/pcl/recognition/impl/cg/hough_3d.hpp @@ -365,7 +365,7 @@ pcl::Hough3DGrouping::re //} this->deinitCompute (); - return (true); + return (transformations.size ()); } diff --git a/recognition/include/pcl/recognition/impl/hv/greedy_verification.hpp b/recognition/include/pcl/recognition/impl/hv/greedy_verification.hpp index 9e2c4fed..f3624dbf 100644 --- a/recognition/include/pcl/recognition/impl/hv/greedy_verification.hpp +++ b/recognition/include/pcl/recognition/impl/hv/greedy_verification.hpp @@ -53,7 +53,7 @@ template // initialize explained_by_RM points_explained_by_rm_.resize (scene_cloud_downsampled_->points.size ()); - // initalize model + // initialize model for (size_t m = 0; m < visible_models_.size (); m++) { boost::shared_ptr < RecognitionModel > recog_model (new RecognitionModel); diff --git a/recognition/include/pcl/recognition/impl/hv/hv_papazov.hpp b/recognition/include/pcl/recognition/impl/hv/hv_papazov.hpp index 8e104bf7..fbca1c1f 100644 --- a/recognition/include/pcl/recognition/impl/hv/hv_papazov.hpp +++ b/recognition/include/pcl/recognition/impl/hv/hv_papazov.hpp @@ -59,7 +59,7 @@ template explained_by_RM_.resize (scene_cloud_downsampled_->points.size ()); points_explained_by_rm_.resize (scene_cloud_downsampled_->points.size ()); - // initalize model + // initialize model for (size_t m = 0; m < complete_models_.size (); m++) { boost::shared_ptr < RecognitionModel > recog_model (new RecognitionModel); @@ -120,7 +120,7 @@ template } else { - mask_[m] = false; // the model didnt survive the sequential check... + mask_[m] = false; // the model didn't survive the sequential check... } } } diff --git a/recognition/include/pcl/recognition/impl/linemod/line_rgbd.hpp b/recognition/include/pcl/recognition/impl/linemod/line_rgbd.hpp index 2d5f5f78..8574ad1a 100644 --- a/recognition/include/pcl/recognition/impl/linemod/line_rgbd.hpp +++ b/recognition/include/pcl/recognition/impl/linemod/line_rgbd.hpp @@ -67,7 +67,7 @@ pcl::LineRGBD::readLTMHeader (int fd, pcl::io::TARHeader & return (false); // We only support regular files for now. - // Addional file types in TAR include: hard links, symbolic links, device/special files, block devices, + // Additional file types in TAR include: hard links, symbolic links, device/special files, block devices, // directories, and named pipes. if (header.file_type[0] != '0' && header.file_type[0] != '\0') return (false); diff --git a/recognition/include/pcl/recognition/implicit_shape_model.h b/recognition/include/pcl/recognition/implicit_shape_model.h index c643ef30..845923c9 100644 --- a/recognition/include/pcl/recognition/implicit_shape_model.h +++ b/recognition/include/pcl/recognition/implicit_shape_model.h @@ -357,7 +357,7 @@ namespace pcl void setTrainingClasses (const std::vector& training_classes); - /** \brief This method returns the coresponding cloud of normals for every training point cloud. */ + /** \brief This method returns the corresponding cloud of normals for every training point cloud. */ std::vector::Ptr> getTrainingNormals (); @@ -405,7 +405,7 @@ namespace pcl /** \brief This method allows to set the value of sigma used for calculating the learned weights for every single class. * \param[in] training_sigmas new sigmas for every class. If you want these values to be computed automatically, * just pass the empty array. The automatic regime calculates the maximum distance between the objects points and takes 10% of - * this value as recomended in the article. If there are several objects of the same class, + * this value as recommended in the article. If there are several objects of the same class, * then it computes the average maximum distance and takes 10%. Note that each class has its own sigma value. */ void @@ -435,7 +435,7 @@ namespace pcl * and returns the list of votes. * \param[in] model trained model which will be used for searching the objects * \param[in] in_cloud input cloud that need to be investigated - * \param[in] in_normals cloud of normals coresponding to the input cloud + * \param[in] in_normals cloud of normals corresponding to the input cloud * \param[in] in_class_of_interest class which we are looking for */ boost::shared_ptr > @@ -567,7 +567,7 @@ namespace pcl void generateRandomCenter (const std::vector >& boxes, Eigen::VectorXf& center); - /** \brief Computes the square distance beetween two vectors. + /** \brief Computes the square distance between two vectors. * \param[in] vec_1 first vector * \param[in] vec_2 second vector */ diff --git a/recognition/include/pcl/recognition/linemod/line_rgbd.h b/recognition/include/pcl/recognition/linemod/line_rgbd.h index 8b1b3f38..c0d6dcec 100644 --- a/recognition/include/pcl/recognition/linemod/line_rgbd.h +++ b/recognition/include/pcl/recognition/linemod/line_rgbd.h @@ -209,7 +209,7 @@ namespace pcl void detect (std::vector::Detection> & detections); - /** \brief Applies the detection process in a semi-scale-invariant manner. This is done by acutally + /** \brief Applies the detection process in a semi-scale-invariant manner. This is done by actually * scaling the template to different sizes. */ void @@ -304,7 +304,7 @@ namespace pcl /** \brief Point clouds corresponding to the templates. */ pcl::PointCloud::CloudVectorType template_point_clouds_; - /** \brief Bounding boxes corresonding to the templates. */ + /** \brief Bounding boxes corresponding to the templates. */ std::vector bounding_boxes_; /** \brief Object IDs corresponding to the templates. */ std::vector object_ids_; diff --git a/recognition/include/pcl/recognition/quantizable_modality.h b/recognition/include/pcl/recognition/quantizable_modality.h index c64617b6..c7dcaee1 100644 --- a/recognition/include/pcl/recognition/quantizable_modality.h +++ b/recognition/include/pcl/recognition/quantizable_modality.h @@ -62,7 +62,7 @@ namespace pcl virtual QuantizedMap & getQuantizedMap () = 0; - /** \brief Returns a reference to the internally computed spreaded quantized map. */ + /** \brief Returns a reference to the internally computed spread quantized map. */ virtual QuantizedMap & getSpreadedQuantizedMap () = 0; diff --git a/recognition/include/pcl/recognition/ransac_based/auxiliary.h b/recognition/include/pcl/recognition/ransac_based/auxiliary.h index 91e775c4..43a6c809 100644 --- a/recognition/include/pcl/recognition/ransac_based/auxiliary.h +++ b/recognition/include/pcl/recognition/ransac_based/auxiliary.h @@ -96,13 +96,13 @@ namespace pcl template void expandBoundingBoxToContainPoint (T bbox[6], const T p[3]) { - if ( p[0] < bbox[0] ) bbox[0] = p[0]; + if ( p[0] < bbox[0] ) bbox[0] = p[0]; else if ( p[0] > bbox[1] ) bbox[1] = p[0]; - if ( p[1] < bbox[2] ) bbox[2] = p[1]; + if ( p[1] < bbox[2] ) bbox[2] = p[1]; else if ( p[1] > bbox[3] ) bbox[3] = p[1]; - if ( p[2] < bbox[4] ) bbox[4] = p[2]; + if ( p[2] < bbox[4] ) bbox[4] = p[2]; else if ( p[2] > bbox[5] ) bbox[5] = p[2]; } diff --git a/recognition/include/pcl/recognition/ransac_based/voxel_structure.h b/recognition/include/pcl/recognition/ransac_based/voxel_structure.h index ed90c4d3..b3819031 100644 --- a/recognition/include/pcl/recognition/ransac_based/voxel_structure.h +++ b/recognition/include/pcl/recognition/ransac_based/voxel_structure.h @@ -160,7 +160,7 @@ namespace pcl T *voxels_; int num_of_voxels_[3], num_of_voxels_xy_plane_, total_num_of_voxels_; REAL bounds_[6]; - REAL spacing_[3]; // spacing betwen the voxel in x, y and z direction = voxel size in x, y and z direction + REAL spacing_[3]; // spacing between the voxel in x, y and z direction = voxel size in x, y and z direction REAL min_center_[3]; // the center of the voxel with integer coordinates (0, 0, 0) }; } // namespace recognition diff --git a/recognition/include/pcl/recognition/surface_normal_modality.h b/recognition/include/pcl/recognition/surface_normal_modality.h index f9b088c3..4e4f876f 100644 --- a/recognition/include/pcl/recognition/surface_normal_modality.h +++ b/recognition/include/pcl/recognition/surface_normal_modality.h @@ -372,7 +372,7 @@ namespace pcl return (filtered_quantized_surface_normals_); } - /** \brief Returns a reference to the internal spreaded quantized map. */ + /** \brief Returns a reference to the internal spread quantized map. */ inline QuantizedMap & getSpreadedQuantizedMap () { @@ -476,7 +476,7 @@ namespace pcl pcl::QuantizedMap quantized_surface_normals_; /** \brief Filtered quantized surface normals. */ pcl::QuantizedMap filtered_quantized_surface_normals_; - /** \brief Spreaded quantized surface normals. */ + /** \brief Spread quantized surface normals. */ pcl::QuantizedMap spreaded_quantized_surface_normals_; /** \brief Map containing surface normal orientations. */ diff --git a/recognition/src/face_detection/face_detector_data_provider.cpp b/recognition/src/face_detection/face_detector_data_provider.cpp index 1df2c0c8..33dfa545 100644 --- a/recognition/src/face_detection/face_detector_data_provider.cpp +++ b/recognition/src/face_detection/face_detector_data_provider.cpp @@ -431,7 +431,7 @@ void pcl::face_detection::FaceDetectorDataProvider::tree_reciprocal_; using CorrespondenceEstimationBase::target_; - /** \brief Internal computation initalization. */ + /** \brief Internal computation initialization. */ bool initCompute (); diff --git a/registration/include/pcl/registration/correspondence_estimation_normal_shooting.h b/registration/include/pcl/registration/correspondence_estimation_normal_shooting.h index 4c5e0abc..7dbc82f9 100644 --- a/registration/include/pcl/registration/correspondence_estimation_normal_shooting.h +++ b/registration/include/pcl/registration/correspondence_estimation_normal_shooting.h @@ -198,7 +198,7 @@ namespace pcl using CorrespondenceEstimationBase::tree_reciprocal_; using CorrespondenceEstimationBase::target_; - /** \brief Internal computation initalization. */ + /** \brief Internal computation initialization. */ bool initCompute (); diff --git a/registration/include/pcl/registration/correspondence_rejection.h b/registration/include/pcl/registration/correspondence_rejection.h index ee6ad53b..9094e4e8 100644 --- a/registration/include/pcl/registration/correspondence_rejection.h +++ b/registration/include/pcl/registration/correspondence_rejection.h @@ -246,19 +246,6 @@ namespace pcl /** \brief Empty destructor */ virtual ~DataContainer () {} - /** \brief Provide a source point cloud dataset (must contain XYZ - * data!), used to compute the correspondence distance. - * \param[in] cloud a cloud containing XYZ data - */ - PCL_DEPRECATED ("[pcl::registration::DataContainer::setInputCloud] setInputCloud is deprecated. Please use setInputSource instead.") - void - setInputCloud (const PointCloudConstPtr &cloud); - - /** \brief Get a pointer to the input point cloud dataset target. */ - PCL_DEPRECATED ("[pcl::registration::DataContainer::getInputCloud] getInputCloud is deprecated. Please use getInputSource instead.") - PointCloudConstPtr const - getInputCloud (); - /** \brief Provide a source point cloud dataset (must contain XYZ * data!), used to compute the correspondence distance. * \param[in] cloud a cloud containing XYZ data @@ -359,7 +346,7 @@ namespace pcl } /** \brief Get the correspondence score for a given pair of correspondent points based on - * the angle betweeen the normals. The normmals for the in put and target clouds must be + * the angle between the normals. The normmals for the in put and target clouds must be * set before using this function * \param[in] corr Correspondent points */ @@ -418,7 +405,5 @@ namespace pcl } } -#include - #endif /* PCL_REGISTRATION_CORRESPONDENCE_REJECTION_H_ */ diff --git a/registration/include/pcl/registration/correspondence_rejection_sample_consensus.h b/registration/include/pcl/registration/correspondence_rejection_sample_consensus.h index 6267ade0..fe9bb0ba 100644 --- a/registration/include/pcl/registration/correspondence_rejection_sample_consensus.h +++ b/registration/include/pcl/registration/correspondence_rejection_sample_consensus.h @@ -97,18 +97,6 @@ namespace pcl getRemainingCorrespondences (const pcl::Correspondences& original_correspondences, pcl::Correspondences& remaining_correspondences); - /** \brief Provide a source point cloud dataset (must contain XYZ data!) - * \param[in] cloud a cloud containing XYZ data - */ - PCL_DEPRECATED ("[pcl::registration::CorrespondenceRejectorSampleConsensus::setInputCloud] setInputCloud is deprecated. Please use setInputSource instead.") - virtual void - setInputCloud (const PointCloudConstPtr &cloud); - - /** \brief Get a pointer to the input point cloud dataset target. */ - PCL_DEPRECATED ("[pcl::registration::CorrespondenceRejectorSampleConsensus::getInputCloud] getInputCloud is deprecated. Please use getInputSource instead.") - PointCloudConstPtr const - getInputCloud (); - /** \brief Provide a source point cloud dataset (must contain XYZ data!) * \param[in] cloud a cloud containing XYZ data */ @@ -122,13 +110,6 @@ namespace pcl inline PointCloudConstPtr const getInputSource () { return (input_); } - /** \brief Provide a target point cloud dataset (must contain XYZ data!) - * \param[in] cloud a cloud containing XYZ data - */ - PCL_DEPRECATED ("[pcl::registration::CorrespondenceRejectorSampleConsensus::setTargetCloud] setTargetCloud is deprecated. Please use setInputTarget instead.") - virtual void - setTargetCloud (const PointCloudConstPtr &cloud); - /** \brief Provide a target point cloud dataset (must contain XYZ data!) * \param[in] cloud a cloud containing XYZ data */ @@ -181,26 +162,12 @@ namespace pcl inline double getInlierThreshold () { return inlier_threshold_; }; - /** \brief Set the maximum number of iterations. - * \param[in] max_iterations Maximum number if iterations to run - */ - PCL_DEPRECATED ("[pcl::registration::CorrespondenceRejectorSampleConsensus::setMaxIterations] setMaxIterations is deprecated. Please use setMaximumIterations instead.") - void - setMaxIterations (int max_iterations); - /** \brief Set the maximum number of iterations. * \param[in] max_iterations Maximum number if iterations to run */ inline void setMaximumIterations (int max_iterations) { max_iterations_ = std::max (max_iterations, 0); } - /** \brief Get the maximum number of iterations. - * \return max_iterations Maximum number if iterations to run - */ - PCL_DEPRECATED ("[pcl::registration::CorrespondenceRejectorSampleConsensus::getMaxIterations] getMaxIterations is deprecated. Please use getMaximumIterations instead.") - int - getMaxIterations (); - /** \brief Get the maximum number of iterations. * \return max_iterations Maximum number if iterations to run */ diff --git a/registration/include/pcl/registration/correspondence_sorting.h b/registration/include/pcl/registration/correspondence_sorting.h index fd2b2fd6..83eb64e0 100644 --- a/registration/include/pcl/registration/correspondence_sorting.h +++ b/registration/include/pcl/registration/correspondence_sorting.h @@ -54,8 +54,11 @@ namespace pcl * \author Dirk Holz * \ingroup registration */ - struct sortCorrespondencesByQueryIndex : public std::binary_function + struct sortCorrespondencesByQueryIndex { + typedef pcl::Correspondence first_argument_type; + typedef pcl::Correspondence second_argument_type; + typedef bool result_type; bool operator()( pcl::Correspondence a, pcl::Correspondence b) { @@ -67,8 +70,11 @@ namespace pcl * \author Dirk Holz * \ingroup registration */ - struct sortCorrespondencesByMatchIndex : public std::binary_function + struct sortCorrespondencesByMatchIndex { + typedef pcl::Correspondence first_argument_type; + typedef pcl::Correspondence second_argument_type; + typedef bool result_type; bool operator()( pcl::Correspondence a, pcl::Correspondence b) { @@ -80,8 +86,11 @@ namespace pcl * \author Dirk Holz * \ingroup registration */ - struct sortCorrespondencesByDistance : public std::binary_function + struct sortCorrespondencesByDistance { + typedef pcl::Correspondence first_argument_type; + typedef pcl::Correspondence second_argument_type; + typedef bool result_type; bool operator()( pcl::Correspondence a, pcl::Correspondence b) { @@ -93,8 +102,11 @@ namespace pcl * \author Dirk Holz * \ingroup registration */ - struct sortCorrespondencesByQueryIndexAndDistance : public std::binary_function + struct sortCorrespondencesByQueryIndexAndDistance { + typedef pcl::Correspondence first_argument_type; + typedef pcl::Correspondence second_argument_type; + typedef bool result_type; inline bool operator()( pcl::Correspondence a, pcl::Correspondence b) { @@ -110,8 +122,11 @@ namespace pcl * \author Dirk Holz * \ingroup registration */ - struct sortCorrespondencesByMatchIndexAndDistance : public std::binary_function + struct sortCorrespondencesByMatchIndexAndDistance { + typedef pcl::Correspondence first_argument_type; + typedef pcl::Correspondence second_argument_type; + typedef bool result_type; inline bool operator()( pcl::Correspondence a, pcl::Correspondence b) { diff --git a/registration/include/pcl/registration/correspondence_types.h b/registration/include/pcl/registration/correspondence_types.h index ef7905b9..57649923 100644 --- a/registration/include/pcl/registration/correspondence_types.h +++ b/registration/include/pcl/registration/correspondence_types.h @@ -50,7 +50,7 @@ namespace pcl * \param[in] correspondences list of correspondences * \param[out] mean the mean descriptor distance of correspondences * \param[out] stddev the standard deviation of descriptor distances. - * \note The sample varaiance is used to determine the standard deviation + * \note The sample variance is used to determine the standard deviation */ inline void getCorDistMeanStd (const pcl::Correspondences& correspondences, double &mean, double &stddev); diff --git a/registration/include/pcl/registration/edge_measurements.h b/registration/include/pcl/registration/edge_measurements.h index f36b0079..5499bdef 100644 --- a/registration/include/pcl/registration/edge_measurements.h +++ b/registration/include/pcl/registration/edge_measurements.h @@ -62,6 +62,7 @@ namespace pcl VertexT v_start, v_end; Eigen::Matrix4f relative_transformation; InformationT information_matrix; + EIGEN_MAKE_ALIGNED_OPERATOR_NEW PoseMeasurement (const VertexT& v_s, const VertexT& v_e, const Eigen::Matrix4f& tr, const InformationT& mtx) : v_start (v_s), v_end (v_e), relative_transformation (tr), information_matrix (mtx) {} diff --git a/registration/include/pcl/registration/exceptions.h b/registration/include/pcl/registration/exceptions.h index 921fb643..875d4417 100644 --- a/registration/include/pcl/registration/exceptions.h +++ b/registration/include/pcl/registration/exceptions.h @@ -59,7 +59,7 @@ namespace pcl } ; /** \class NotEnoughPointsException - * \brief An exception that is thrown when the number of correspondants is not equal + * \brief An exception that is thrown when the number of correspondents is not equal * to the minimum required */ class PCL_EXPORTS NotEnoughPointsException : public PCLException diff --git a/registration/include/pcl/registration/gicp.h b/registration/include/pcl/registration/gicp.h index 361e5043..3f1c0643 100644 --- a/registration/include/pcl/registration/gicp.h +++ b/registration/include/pcl/registration/gicp.h @@ -120,13 +120,6 @@ namespace pcl boost::bind (&GeneralizedIterativeClosestPoint::estimateRigidTransformationBFGS, this, _1, _2, _3, _4, _5); } - - /** \brief Provide a pointer to the input dataset - * \param cloud the const boost shared pointer to a PointCloud message - */ - PCL_DEPRECATED ("[pcl::registration::GeneralizedIterativeClosestPoint::setInputCloud] setInputCloud is deprecated. Please use setInputSource instead.") - void - setInputCloud (const PointCloudSourceConstPtr &cloud); /** \brief Provide a pointer to the input dataset * \param cloud the const boost shared pointer to a PointCloud message @@ -186,7 +179,7 @@ namespace pcl * \param[in] cloud_src the source point cloud dataset * \param[in] indices_src the vector of indices describing the points of interest in \a cloud_src * \param[in] cloud_tgt the target point cloud dataset - * \param[in] indices_tgt the vector of indices describing the correspondences of the interst points from \a indices_src + * \param[in] indices_tgt the vector of indices describing the correspondences of the interest points from \a indices_src * \param[out] transformation_matrix the resultant transformation matrix */ void @@ -260,7 +253,7 @@ namespace pcl int k_correspondences_; /** \brief The epsilon constant for gicp paper; this is NOT the convergence - * tolerence + * tolerance * default: 0.001 */ double gicp_epsilon_; @@ -300,7 +293,7 @@ namespace pcl int max_inner_iterations_; /** \brief compute points covariances matrices according to the K nearest - * neighbors. K is set via setCorrespondenceRandomness() methode. + * neighbors. K is set via setCorrespondenceRandomness() method. * \param cloud pointer to point cloud * \param tree KD tree performer for nearest neighbors search * \param[out] cloud_covariances covariances matrices for each point in the cloud diff --git a/registration/include/pcl/registration/graph_handler.h b/registration/include/pcl/registration/graph_handler.h index caa2f1f6..814bb09a 100644 --- a/registration/include/pcl/registration/graph_handler.h +++ b/registration/include/pcl/registration/graph_handler.h @@ -51,7 +51,7 @@ namespace pcl namespace registration { /** \brief @b GraphHandler class is a wrapper for a general SLAM graph - * The actual graph class must fulfil the following boost::graph concepts: + * The actual graph class must fulfill the following boost::graph concepts: * - BidirectionalGraph * - AdjacencyGraph * - VertexAndEdgeListGraph diff --git a/registration/include/pcl/registration/ia_fpcs.h b/registration/include/pcl/registration/ia_fpcs.h index 6325a6b6..6691f246 100644 --- a/registration/include/pcl/registration/ia_fpcs.h +++ b/registration/include/pcl/registration/ia_fpcs.h @@ -515,7 +515,7 @@ namespace pcl /** \brief Estimated squared metric overlap between source and target. * \note Internally calculated using the estimated overlap and the extent of the source cloud. * It is used to derive the minimum sampling distance of the base points as well as to calculated - * the number of trys to reliable find a correct mach. + * the number of tries to reliably find a correct match. */ float max_base_diameter_sqr_; diff --git a/registration/include/pcl/registration/icp.h b/registration/include/pcl/registration/icp.h index 58abcdab..536fd5a6 100644 --- a/registration/include/pcl/registration/icp.h +++ b/registration/include/pcl/registration/icp.h @@ -119,6 +119,7 @@ namespace pcl using Registration::final_transformation_; using Registration::transformation_; using Registration::transformation_epsilon_; + using Registration::transformation_rotation_epsilon_; using Registration::converged_; using Registration::corr_dist_threshold_; using Registration::inlier_threshold_; diff --git a/registration/include/pcl/registration/impl/correspondence_estimation.hpp b/registration/include/pcl/registration/impl/correspondence_estimation.hpp index bdc2e87b..3eae7d1f 100644 --- a/registration/include/pcl/registration/impl/correspondence_estimation.hpp +++ b/registration/include/pcl/registration/impl/correspondence_estimation.hpp @@ -43,20 +43,6 @@ #include #include -/////////////////////////////////////////////////////////////////////////////////////////// -template void -pcl::registration::CorrespondenceEstimationBase::setInputCloud (const typename pcl::registration::CorrespondenceEstimationBase::PointCloudSourceConstPtr &cloud) -{ - setInputSource (cloud); -} - -/////////////////////////////////////////////////////////////////////////////////////////// -template typename pcl::registration::CorrespondenceEstimationBase::PointCloudSourceConstPtr const -pcl::registration::CorrespondenceEstimationBase::getInputCloud () -{ - return (getInputSource ()); -} - /////////////////////////////////////////////////////////////////////////////////////////// template void pcl::registration::CorrespondenceEstimationBase::setInputTarget ( diff --git a/registration/include/pcl/registration/impl/correspondence_rejection_sample_consensus.hpp b/registration/include/pcl/registration/impl/correspondence_rejection_sample_consensus.hpp index 7ab5a2d9..6b96a1b6 100644 --- a/registration/include/pcl/registration/impl/correspondence_rejection_sample_consensus.hpp +++ b/registration/include/pcl/registration/impl/correspondence_rejection_sample_consensus.hpp @@ -42,44 +42,6 @@ #include -/////////////////////////////////////////////////////////////////////////////////////////// -template void -pcl::registration::CorrespondenceRejectorSampleConsensus::setInputCloud ( - const typename pcl::registration::CorrespondenceRejectorSampleConsensus::PointCloudConstPtr &cloud) -{ - setInputSource (cloud); -} - -/////////////////////////////////////////////////////////////////////////////////////////// -template typename pcl::registration::CorrespondenceRejectorSampleConsensus::PointCloudConstPtr const -pcl::registration::CorrespondenceRejectorSampleConsensus::getInputCloud () -{ - return (getInputSource ()); -} - -/////////////////////////////////////////////////////////////////////////////////////////// -template void -pcl::registration::CorrespondenceRejectorSampleConsensus::setTargetCloud ( - const typename pcl::registration::CorrespondenceRejectorSampleConsensus::PointCloudConstPtr &cloud) -{ - setInputTarget (cloud); -} - -/////////////////////////////////////////////////////////////////////////////////////////// -template void -pcl::registration::CorrespondenceRejectorSampleConsensus::setMaxIterations ( - int max_iterations) -{ - setMaximumIterations (max_iterations); -} - -/////////////////////////////////////////////////////////////////////////////////////////// -template int -pcl::registration::CorrespondenceRejectorSampleConsensus::getMaxIterations () -{ - return (getMaximumIterations ()); -} - /////////////////////////////////////////////////////////////////////////////////////////// template void pcl::registration::CorrespondenceRejectorSampleConsensus::getRemainingCorrespondences ( diff --git a/registration/include/pcl/registration/impl/default_convergence_criteria.hpp b/registration/include/pcl/registration/impl/default_convergence_criteria.hpp index 395709a9..22819d8b 100644 --- a/registration/include/pcl/registration/impl/default_convergence_criteria.hpp +++ b/registration/include/pcl/registration/impl/default_convergence_criteria.hpp @@ -54,12 +54,9 @@ pcl::registration::DefaultConvergenceCriteria::hasConverged () { if (failure_after_max_iter_) return (false); - else - { - convergence_state_ = CONVERGENCE_CRITERIA_ITERATIONS; - return (true); - } - return (failure_after_max_iter_ ? false : true); + + convergence_state_ = CONVERGENCE_CRITERIA_ITERATIONS; + return (true); } // 2. The epsilon (difference) between the previous transformation and the current estimated transformation diff --git a/registration/include/pcl/registration/impl/gicp.hpp b/registration/include/pcl/registration/impl/gicp.hpp index 70e6c98d..1afc0713 100644 --- a/registration/include/pcl/registration/impl/gicp.hpp +++ b/registration/include/pcl/registration/impl/gicp.hpp @@ -43,14 +43,6 @@ #include #include -/////////////////////////////////////////////////////////////////////////////////////////// -template void -pcl::GeneralizedIterativeClosestPoint::setInputCloud ( - const typename pcl::GeneralizedIterativeClosestPoint::PointCloudSourceConstPtr &cloud) -{ - setInputSource (cloud); -} - //////////////////////////////////////////////////////////////////////////////////////// template template void @@ -266,7 +258,7 @@ pcl::GeneralizedIterativeClosestPoint::OptimizationFun Vector4fMapConst p_tgt = gicp_->tmp_tgt_->points[(*gicp_->tmp_idx_tgt_)[i]].getVector4fMap (); Eigen::Vector4f pp (transformation_matrix * p_src); // Estimate the distance (cost function) - // The last coordiante is still guaranteed to be set to 1.0 + // The last coordinate is still guaranteed to be set to 1.0 Eigen::Vector3d res(pp[0] - p_tgt[0], pp[1] - p_tgt[1], pp[2] - p_tgt[2]); Eigen::Vector3d temp (gicp_->mahalanobis((*gicp_->tmp_idx_src_)[i]) * res); //increment= res'*temp/num_matches = temp'*M*temp/num_matches (we postpone 1/num_matches after the loop closes) @@ -294,7 +286,7 @@ pcl::GeneralizedIterativeClosestPoint::OptimizationFun Vector4fMapConst p_tgt = gicp_->tmp_tgt_->points[(*gicp_->tmp_idx_tgt_)[i]].getVector4fMap (); Eigen::Vector4f pp (transformation_matrix * p_src); - // The last coordiante is still guaranteed to be set to 1.0 + // The last coordinate is still guaranteed to be set to 1.0 Eigen::Vector3d res (pp[0] - p_tgt[0], pp[1] - p_tgt[1], pp[2] - p_tgt[2]); // temp = M*res Eigen::Vector3d temp (gicp_->mahalanobis ((*gicp_->tmp_idx_src_)[i]) * res); @@ -328,7 +320,7 @@ pcl::GeneralizedIterativeClosestPoint::OptimizationFun // The last coordinate, p_tgt[3] is guaranteed to be set to 1.0 in registration.hpp Vector4fMapConst p_tgt = gicp_->tmp_tgt_->points[(*gicp_->tmp_idx_tgt_)[i]].getVector4fMap (); Eigen::Vector4f pp (transformation_matrix * p_src); - // The last coordiante is still guaranteed to be set to 1.0 + // The last coordinate is still guaranteed to be set to 1.0 Eigen::Vector3d res (pp[0] - p_tgt[0], pp[1] - p_tgt[1], pp[2] - p_tgt[2]); // temp = M*res Eigen::Vector3d temp (gicp_->mahalanobis((*gicp_->tmp_idx_src_)[i]) * res); diff --git a/registration/include/pcl/registration/impl/ia_fpcs.hpp b/registration/include/pcl/registration/impl/ia_fpcs.hpp index 52c6dad3..76407f23 100644 --- a/registration/include/pcl/registration/impl/ia_fpcs.hpp +++ b/registration/include/pcl/registration/impl/ia_fpcs.hpp @@ -187,7 +187,7 @@ pcl::registration::FPCSInitialAlignment 0 ? 0 : -1); } @@ -787,7 +787,7 @@ pcl::registration::FPCSInitialAlignment points[*it_base], centre_pt_base); float best_diff_sqr = FLT_MAX; - int best_index; + int best_index = -1; for (it_match = copy.begin (); it_match != it_match_e; it_match++) { @@ -868,7 +868,7 @@ pcl::registration::FPCSInitialAlignment (nr_points); float fitness_score_temp = 1.f - inlier_score_temp; @@ -885,7 +885,7 @@ template ::finalCompute ( const std::vector &candidates) { - // get best fitness_score over all trys + // get best fitness_score over all tries int nr_candidates = static_cast (candidates.size ()); int best_index = -1; float best_score = FLT_MAX; diff --git a/registration/include/pcl/registration/impl/ia_kfpcs.hpp b/registration/include/pcl/registration/impl/ia_kfpcs.hpp index ece1a493..7bac3125 100644 --- a/registration/include/pcl/registration/impl/ia_kfpcs.hpp +++ b/registration/include/pcl/registration/impl/ia_kfpcs.hpp @@ -83,7 +83,7 @@ pcl::registration::KFPCSInitialAlignment size (); - if (nr_indices < ransac_iterations_) + if (nr_indices < size_t (ransac_iterations_)) indices_validation_ = indices_; else for (int i = 0; i < ransac_iterations_; i++) @@ -164,7 +164,7 @@ pcl::registration::KFPCSInitialAlignment fitness_score) return (-1); @@ -193,7 +193,7 @@ pcl::registration::KFPCSInitialAlignment begin (), it_curr_e = it->end (); it_curr != it_curr_e; it_curr++) candidates_.push_back (*it_curr); - // sort acoording to score value + // sort according to score value std::sort (candidates_.begin (), candidates_.end (), by_score ()); // return here if no score was valid, i.e. all scores are FLT_MAX diff --git a/registration/include/pcl/registration/impl/ia_ransac.hpp b/registration/include/pcl/registration/impl/ia_ransac.hpp index 180572c6..b24a3944 100644 --- a/registration/include/pcl/registration/impl/ia_ransac.hpp +++ b/registration/include/pcl/registration/impl/ia_ransac.hpp @@ -235,7 +235,7 @@ pcl::SampleConsensusInitialAlignment::comput // Estimate the transform from the samples to their corresponding points transformation_estimation_->estimateRigidTransformation (*input_, sample_indices, *target_, corresponding_indices, transformation_); - // Tranform the data and compute the error + // Transform the data and compute the error transformPointCloud (*input_, input_transformed, transformation_); error = computeErrorMetric (input_transformed, static_cast (corr_dist_threshold_)); diff --git a/registration/include/pcl/registration/impl/icp.hpp b/registration/include/pcl/registration/impl/icp.hpp index 84626b99..bbc69d5b 100644 --- a/registration/include/pcl/registration/impl/icp.hpp +++ b/registration/include/pcl/registration/impl/icp.hpp @@ -163,7 +163,10 @@ pcl::IterativeClosestPoint::computeTransformat convergence_criteria_->setMaximumIterations (max_iterations_); convergence_criteria_->setRelativeMSE (euclidean_fitness_epsilon_); convergence_criteria_->setTranslationThreshold (transformation_epsilon_); - convergence_criteria_->setRotationThreshold (1.0 - transformation_epsilon_); + if (transformation_rotation_epsilon_ > 0) + convergence_criteria_->setRotationThreshold (transformation_rotation_epsilon_); + else + convergence_criteria_->setRotationThreshold (1.0 - transformation_epsilon_); // Repeat until convergence do @@ -218,7 +221,7 @@ pcl::IterativeClosestPoint::computeTransformat // Estimate the transform transformation_estimation_->estimateRigidTransformation (*input_transformed, *target_, *correspondences_, transformation_); - // Tranform the data + // Transform the data transformCloud (*input_transformed, *input_transformed, transformation_); // Obtain the final transformation diff --git a/registration/include/pcl/registration/impl/joint_icp.hpp b/registration/include/pcl/registration/impl/joint_icp.hpp index a3623c12..9245ecf6 100644 --- a/registration/include/pcl/registration/impl/joint_icp.hpp +++ b/registration/include/pcl/registration/impl/joint_icp.hpp @@ -236,7 +236,7 @@ pcl::JointIterativeClosestPoint::computeTransf // Estimate the transform jointly, on a combined correspondence set transformation_estimation_->estimateRigidTransformation (*inputs_transformed_combined, *targets_combined, *correspondences_, transformation_); - // Tranform the combined data + // Transform the combined data this->transformCloud (*inputs_transformed_combined, *inputs_transformed_combined, transformation_); // And all its components for (size_t i = 0; i < sources_.size (); i++) diff --git a/registration/include/pcl/registration/impl/ndt.hpp b/registration/include/pcl/registration/impl/ndt.hpp index b464fce4..0ef10504 100644 --- a/registration/include/pcl/registration/impl/ndt.hpp +++ b/registration/include/pcl/registration/impl/ndt.hpp @@ -61,7 +61,7 @@ pcl::NormalDistributionsTransform::NormalDistributions double gauss_c1, gauss_c2, gauss_d3; - // Initializes the guassian fitting parameters (eq. 6.8) [Magnusson 2009] + // Initializes the gaussian fitting parameters (eq. 6.8) [Magnusson 2009] gauss_c1 = 10.0 * (1 - outlier_ratio_); gauss_c2 = outlier_ratio_ / pow (resolution_, 3); gauss_d3 = -log (gauss_c2); @@ -81,7 +81,7 @@ pcl::NormalDistributionsTransform::computeTransformati double gauss_c1, gauss_c2, gauss_d3; - // Initializes the guassian fitting parameters (eq. 6.8) [Magnusson 2009] + // Initializes the gaussian fitting parameters (eq. 6.8) [Magnusson 2009] gauss_c1 = 10 * (1 - outlier_ratio_); gauss_c2 = outlier_ratio_ / pow (resolution_, 3); gauss_d3 = -log (gauss_c2); @@ -156,14 +156,20 @@ pcl::NormalDistributionsTransform::computeTransformati if (update_visualizer_ != 0) update_visualizer_ (output, std::vector(), *target_, std::vector() ); - if (nr_iterations_ > max_iterations_ || - (nr_iterations_ && (std::fabs (delta_p_norm) < transformation_epsilon_))) - { - converged_ = true; - } + double cos_angle = 0.5 * (transformation_.coeff (0, 0) + transformation_.coeff (1, 1) + transformation_.coeff (2, 2) - 1); + double translation_sqr = transformation_.coeff (0, 3) * transformation_.coeff (0, 3) + + transformation_.coeff (1, 3) * transformation_.coeff (1, 3) + + transformation_.coeff (2, 3) * transformation_.coeff (2, 3); nr_iterations_++; + if (nr_iterations_ >= max_iterations_ || + ((transformation_epsilon_ > 0 && translation_sqr <= transformation_epsilon_) && (transformation_rotation_epsilon_ > 0 && cos_angle >= transformation_rotation_epsilon_)) || + ((transformation_epsilon_ <= 0) && (transformation_rotation_epsilon_ > 0 && cos_angle >= transformation_rotation_epsilon_)) || + ((transformation_epsilon_ > 0 && translation_sqr <= transformation_epsilon_) && (transformation_rotation_epsilon_ <= 0))) + { + converged_ = true; + } } // Store transformation probability. The realtive differences within each scan registration are accurate @@ -356,7 +362,7 @@ pcl::NormalDistributionsTransform::updateDerivatives ( Eigen::Vector3d cov_dxd_pi; // e^(-d_2/2 * (x_k - mu_k)^T Sigma_k^-1 (x_k - mu_k)) Equation 6.9 [Magnusson 2009] double e_x_cov_x = exp (-gauss_d2_ * x_trans.dot (c_inv * x_trans) / 2); - // Calculate probability of transtormed points existance, Equation 6.9 [Magnusson 2009] + // Calculate probability of transformed points existence, Equation 6.9 [Magnusson 2009] double score_inc = -gauss_d1_ * e_x_cov_x; e_x_cov_x = gauss_d2_ * e_x_cov_x; @@ -680,7 +686,7 @@ pcl::NormalDistributionsTransform::computeStepLengthMT // Iterate until max number of iterations, interval convergance or a value satisfies the sufficient decrease, Equation 1.1, and curvature condition, Equation 1.2 [More, Thuente 1994] while (!interval_converged && step_iterations < max_step_iterations && !(psi_t <= 0 /*Sufficient Decrease*/ && d_phi_t <= -nu * d_phi_0 /*Curvature Condition*/)) { - // Use auxilary function if interval I is not closed + // Use auxiliary function if interval I is not closed if (open_interval) { a_t = trialValueSelectionMT (a_l, f_l, g_l, diff --git a/registration/include/pcl/registration/impl/ndt_2d.hpp b/registration/include/pcl/registration/impl/ndt_2d.hpp index 0b66bcea..cfa97c85 100644 --- a/registration/include/pcl/registration/impl/ndt_2d.hpp +++ b/registration/include/pcl/registration/impl/ndt_2d.hpp @@ -475,9 +475,19 @@ pcl::NormalDistributionsTransform2D::computeTransforma //std::cout << "eps=" << fabs ((transformation - previous_transformation_).sum ()) << std::endl; - if (nr_iterations_ > max_iterations_ || - (transformation - previous_transformation_).array ().abs ().sum () < transformation_epsilon_) + Eigen::Matrix4f transformation_delta = transformation.inverse() * previous_transformation_; + double cos_angle = 0.5 * (transformation_delta.coeff (0, 0) + transformation_delta.coeff (1, 1) + transformation_delta.coeff (2, 2) - 1); + double translation_sqr = transformation_delta.coeff (0, 3) * transformation_delta.coeff (0, 3) + + transformation_delta.coeff (1, 3) * transformation_delta.coeff (1, 3) + + transformation_delta.coeff (2, 3) * transformation_delta.coeff (2, 3); + + if (nr_iterations_ >= max_iterations_ || + ((transformation_epsilon_ > 0 && translation_sqr <= transformation_epsilon_) && (transformation_rotation_epsilon_ > 0 && cos_angle >= transformation_rotation_epsilon_)) || + ((transformation_epsilon_ <= 0) && (transformation_rotation_epsilon_ > 0 && cos_angle >= transformation_rotation_epsilon_)) || + ((transformation_epsilon_ > 0 && translation_sqr <= transformation_epsilon_) && (transformation_rotation_epsilon_ <= 0))) + { converged_ = true; + } } final_transformation_ = transformation; output = intm_cloud; diff --git a/registration/include/pcl/registration/impl/registration.hpp b/registration/include/pcl/registration/impl/registration.hpp index bbee1733..8df1672d 100644 --- a/registration/include/pcl/registration/impl/registration.hpp +++ b/registration/include/pcl/registration/impl/registration.hpp @@ -38,21 +38,6 @@ * */ -/////////////////////////////////////////////////////////////////////////////////////////// -template void -pcl::Registration::setInputCloud ( - const typename pcl::Registration::PointCloudSourceConstPtr &cloud) -{ - setInputSource (cloud); -} - -/////////////////////////////////////////////////////////////////////////////////////////// -template typename pcl::Registration::PointCloudSourceConstPtr const -pcl::Registration::getInputCloud () -{ - return (getInputSource ()); -} - /////////////////////////////////////////////////////////////////////////////////////////// template inline void pcl::Registration::setInputTarget (const PointCloudTargetConstPtr &cloud) diff --git a/registration/include/pcl/registration/ndt.h b/registration/include/pcl/registration/ndt.h index fe7d20c6..3c9fa05c 100644 --- a/registration/include/pcl/registration/ndt.h +++ b/registration/include/pcl/registration/ndt.h @@ -225,6 +225,7 @@ namespace pcl using Registration::final_transformation_; using Registration::transformation_; using Registration::transformation_epsilon_; + using Registration::transformation_rotation_epsilon_; using Registration::converged_; using Registration::corr_dist_threshold_; using Registration::inlier_threshold_; @@ -232,7 +233,7 @@ namespace pcl using Registration::update_visualizer_; /** \brief Estimate the transformation and returns the transformed source (input) as output. - * \param[out] output the resultant input transfomed point cloud dataset + * \param[out] output the resultant input transformed point cloud dataset */ virtual void computeTransformation (PointCloudSource &output) @@ -241,7 +242,7 @@ namespace pcl } /** \brief Estimate the transformation and returns the transformed source (input) as output. - * \param[out] output the resultant input transfomed point cloud dataset + * \param[out] output the resultant input transformed point cloud dataset * \param[in] guess the initial gross estimation of the transformation */ virtual void @@ -347,7 +348,7 @@ namespace pcl PointCloudSource &trans_cloud); /** \brief Update interval of possible step lengths for More-Thuente method, \f$ I \f$ in More-Thuente (1994) - * \note Updating Algorithm until some value satifies \f$ \psi(\alpha_k) \leq 0 \f$ and \f$ \phi'(\alpha_k) \geq 0 \f$ + * \note Updating Algorithm until some value satisfies \f$ \psi(\alpha_k) \leq 0 \f$ and \f$ \phi'(\alpha_k) \geq 0 \f$ * and Modified Updating Algorithm from then on [More, Thuente 1994]. * \param[in,out] a_l first endpoint of interval \f$ I \f$, \f$ \alpha_l \f$ in Moore-Thuente (1994) * \param[in,out] f_l value at first endpoint, \f$ f_l \f$ in Moore-Thuente (1994), \f$ \psi(\alpha_l) \f$ for Update Algorithm and \f$ \phi(\alpha_l) \f$ for Modified Update Algorithm @@ -367,7 +368,7 @@ namespace pcl /** \brief Select new trial value for More-Thuente method. * \note Trial Value Selection [More, Thuente 1994], \f$ \psi(\alpha_k) \f$ is used for \f$ f_k \f$ and \f$ g_k \f$ - * until some value satifies the test \f$ \psi(\alpha_k) \leq 0 \f$ and \f$ \phi'(\alpha_k) \geq 0 \f$ + * until some value satisfies the test \f$ \psi(\alpha_k) \leq 0 \f$ and \f$ \phi'(\alpha_k) \geq 0 \f$ * then \f$ \phi(\alpha_k) \f$ is used from then on. * \note Interpolation Minimizer equations from Optimization Theory and Methods: Nonlinear Programming By Wenyu Sun, Ya-xiang Yuan (89-100). * \param[in] a_l first endpoint of interval \f$ I \f$, \f$ \alpha_l \f$ in Moore-Thuente (1994) @@ -386,14 +387,14 @@ namespace pcl double a_u, double f_u, double g_u, double a_t, double f_t, double g_t); - /** \brief Auxilary function used to determin endpoints of More-Thuente interval. + /** \brief Auxiliary function used to determine endpoints of More-Thuente interval. * \note \f$ \psi(\alpha) \f$ in Equation 1.6 (Moore, Thuente 1994) * \param[in] a the step length, \f$ \alpha \f$ in More-Thuente (1994) * \param[in] f_a function value at step length a, \f$ \phi(\alpha) \f$ in More-Thuente (1994) * \param[in] f_0 initial function value, \f$ \phi(0) \f$ in Moore-Thuente (1994) * \param[in] g_0 initial function gradiant, \f$ \phi'(0) \f$ in More-Thuente (1994) * \param[in] mu the step length, constant \f$ \mu \f$ in Equation 1.1 [More, Thuente 1994] - * \return sufficent decrease value + * \return sufficient decrease value */ inline double auxilaryFunction_PsiMT (double a, double f_a, double f_0, double g_0, double mu = 1.e-4) @@ -401,12 +402,12 @@ namespace pcl return (f_a - f_0 - mu * g_0 * a); } - /** \brief Auxilary function derivative used to determin endpoints of More-Thuente interval. + /** \brief Auxiliary function derivative used to determine endpoints of More-Thuente interval. * \note \f$ \psi'(\alpha) \f$, derivative of Equation 1.6 (Moore, Thuente 1994) * \param[in] g_a function gradient at step length a, \f$ \phi'(\alpha) \f$ in More-Thuente (1994) * \param[in] g_0 initial function gradiant, \f$ \phi'(0) \f$ in More-Thuente (1994) * \param[in] mu the step length, constant \f$ \mu \f$ in Equation 1.1 [More, Thuente 1994] - * \return sufficent decrease derivative + * \return sufficient decrease derivative */ inline double auxilaryFunction_dPsiMT (double g_a, double g_0, double mu = 1.e-4) diff --git a/registration/include/pcl/registration/ndt_2d.h b/registration/include/pcl/registration/ndt_2d.h index ce29a2a0..a0643b78 100644 --- a/registration/include/pcl/registration/ndt_2d.h +++ b/registration/include/pcl/registration/ndt_2d.h @@ -135,8 +135,9 @@ namespace pcl using Registration::nr_iterations_; using Registration::max_iterations_; using Registration::transformation_epsilon_; + using Registration::transformation_rotation_epsilon_; using Registration::transformation_; - using Registration::previous_transformation_; + using Registration::previous_transformation_; using Registration::final_transformation_; using Registration::update_visualizer_; using Registration::indices_; diff --git a/registration/include/pcl/registration/pyramid_feature_matching.h b/registration/include/pcl/registration/pyramid_feature_matching.h index 17961572..dda7e14e 100644 --- a/registration/include/pcl/registration/pyramid_feature_matching.h +++ b/registration/include/pcl/registration/pyramid_feature_matching.h @@ -119,7 +119,7 @@ namespace pcl isComputed () { return is_computed_; } /** \brief Static method for comparing two pyramid histograms that returns a floating point value between 0 and 1, - * representing the similiarity between the feature sets on which the two pyramid histograms are based. + * representing the similarity between the feature sets on which the two pyramid histograms are based. * \param pyramid_a Pointer to the first pyramid to be compared (needs to be computed already). * \param pyramid_b Pointer to the second pyramid to be compared (needs to be computed already). */ diff --git a/registration/include/pcl/registration/registration.h b/registration/include/pcl/registration/registration.h index 8a2c5bfe..d291d73e 100644 --- a/registration/include/pcl/registration/registration.h +++ b/registration/include/pcl/registration/registration.h @@ -110,6 +110,7 @@ namespace pcl , transformation_ (Matrix4::Identity ()) , previous_transformation_ (Matrix4::Identity ()) , transformation_epsilon_ (0.0) + , transformation_rotation_epsilon_(0.0) , euclidean_fitness_epsilon_ (-std::numeric_limits::max ()) , corr_dist_threshold_ (std::sqrt (std::numeric_limits::max ())) , inlier_threshold_ (0.05) @@ -123,7 +124,6 @@ namespace pcl , source_cloud_updated_ (true) , force_no_recompute_ (false) , force_no_recompute_reciprocal_ (false) - , update_visualizer_ (NULL) , point_representation_ () { } @@ -173,20 +173,6 @@ namespace pcl void setCorrespondenceEstimation (const CorrespondenceEstimationPtr &ce) { correspondence_estimation_ = ce; } - /** \brief Provide a pointer to the input source - * (e.g., the point cloud that we want to align to the target) - * - * \param[in] cloud the input point cloud source - */ - PCL_DEPRECATED ("[pcl::registration::Registration::setInputCloud] setInputCloud is deprecated. Please use setInputSource instead.") - void - setInputCloud (const PointCloudSourceConstPtr &cloud); - - /** \brief Get a pointer to the input point cloud dataset target. */ - PCL_DEPRECATED ("[pcl::registration::Registration::getInputCloud] getInputCloud is deprecated. Please use getInputSource instead.") - PointCloudSourceConstPtr const - getInputCloud (); - /** \brief Provide a pointer to the input source * (e.g., the point cloud that we want to align to the target) * @@ -326,7 +312,7 @@ namespace pcl inline double getMaxCorrespondenceDistance () { return (corr_dist_threshold_); } - /** \brief Set the transformation epsilon (maximum allowable difference between two consecutive + /** \brief Set the transformation epsilon (maximum allowable translation squared difference between two consecutive * transformations) in order for an optimization to be considered as having converged to the final * solution. * \param[in] epsilon the transformation epsilon in order for an optimization to be considered as having @@ -335,12 +321,27 @@ namespace pcl inline void setTransformationEpsilon (double epsilon) { transformation_epsilon_ = epsilon; } - /** \brief Get the transformation epsilon (maximum allowable difference between two consecutive + /** \brief Get the transformation epsilon (maximum allowable translation squared difference between two consecutive * transformations) as set by the user. */ inline double getTransformationEpsilon () { return (transformation_epsilon_); } + /** \brief Set the transformation rotation epsilon (maximum allowable rotation difference between two consecutive + * transformations) in order for an optimization to be considered as having converged to the final + * solution. + * \param[in] epsilon the transformation rotation epsilon in order for an optimization to be considered as having + * converged to the final solution (epsilon is the cos(angle) in a axis-angle representation). + */ + inline void + setTransformationRotationEpsilon (double epsilon) { transformation_rotation_epsilon_ = epsilon; } + + /** \brief Get the transformation rotation epsilon (maximum allowable difference between two consecutive + * transformations) as set by the user (epsilon is the cos(angle) in a axis-angle representation). + */ + inline double + getTransformationRotationEpsilon () { return (transformation_rotation_epsilon_); } + /** \brief Set the maximum allowed Euclidean error between two consecutive steps in the ICP loop, before * the algorithm is considered to have converged. * The error is estimated as the sum of the differences between correspondences in an Euclidean sense, @@ -348,7 +349,6 @@ namespace pcl * \param[in] epsilon the maximum allowed distance error before the algorithm will be considered to have * converged */ - inline void setEuclideanFitnessEpsilon (double epsilon) { euclidean_fitness_epsilon_ = epsilon; } @@ -404,14 +404,14 @@ namespace pcl /** \brief Call the registration algorithm which estimates the transformation and returns the transformed source * (input) as \a output. - * \param[out] output the resultant input transfomed point cloud dataset + * \param[out] output the resultant input transformed point cloud dataset */ inline void align (PointCloudSource &output); /** \brief Call the registration algorithm which estimates the transformation and returns the transformed source * (input) as \a output. - * \param[out] output the resultant input transfomed point cloud dataset + * \param[out] output the resultant input transformed point cloud dataset * \param[in] guess the initial gross estimation of the transformation */ inline void @@ -421,7 +421,7 @@ namespace pcl inline const std::string& getClassName () const { return (reg_name_); } - /** \brief Internal computation initalization. */ + /** \brief Internal computation initialization. */ bool initCompute (); @@ -515,6 +515,11 @@ namespace pcl */ double transformation_epsilon_; + /** \brief The maximum rotation difference between two consecutive transformations in order to consider convergence + * (user defined). + */ + double transformation_rotation_epsilon_; + /** \brief The maximum allowed Euclidean error between two consecutive steps in the ICP loop, before the * algorithm is considered to have converged. The error is estimated as the sum of the differences between * correspondences in an Euclidean sense, divided by the number of correspondences. @@ -522,7 +527,7 @@ namespace pcl double euclidean_fitness_epsilon_; /** \brief The maximum distance threshold between two correspondent points in source <-> target. If the - * distance is larger than this threshold, the points will be ignored in the alignement process. + * distance is larger than this threshold, the points will be ignored in the alignment process. */ double corr_dist_threshold_; diff --git a/registration/include/pcl/registration/transformation_estimation.h b/registration/include/pcl/registration/transformation_estimation.h index 1ad3b434..55e390c2 100644 --- a/registration/include/pcl/registration/transformation_estimation.h +++ b/registration/include/pcl/registration/transformation_estimation.h @@ -95,7 +95,7 @@ namespace pcl * \param[in] cloud_src the source point cloud dataset * \param[in] indices_src the vector of indices describing the points of interest in \a cloud_src * \param[in] cloud_tgt the target point cloud dataset - * \param[in] indices_tgt the vector of indices describing the correspondences of the interst points from \a indices_src + * \param[in] indices_tgt the vector of indices describing the correspondences of the interest points from \a indices_src * \param[out] transformation_matrix the resultant transformation matrix */ virtual void diff --git a/registration/include/pcl/registration/transformation_estimation_2D.h b/registration/include/pcl/registration/transformation_estimation_2D.h index 9ed9c01d..4bdd96fb 100644 --- a/registration/include/pcl/registration/transformation_estimation_2D.h +++ b/registration/include/pcl/registration/transformation_estimation_2D.h @@ -95,7 +95,7 @@ namespace pcl * \param[in] cloud_src the source point cloud dataset * \param[in] indices_src the vector of indices describing the points of interest in \a cloud_src * \param[in] cloud_tgt the target point cloud dataset - * \param[in] indices_tgt the vector of indices describing the correspondences of the interst points from \a indices_src + * \param[in] indices_tgt the vector of indices describing the correspondences of the interest points from \a indices_src * \param[out] transformation_matrix the resultant transformation matrix */ virtual void diff --git a/registration/include/pcl/registration/transformation_estimation_3point.h b/registration/include/pcl/registration/transformation_estimation_3point.h index d124ef18..43226fb7 100644 --- a/registration/include/pcl/registration/transformation_estimation_3point.h +++ b/registration/include/pcl/registration/transformation_estimation_3point.h @@ -101,7 +101,7 @@ namespace pcl * \param[in] cloud_src the source point cloud dataset * \param[in] indices_src the vector of indices describing the points of interest in \a cloud_src * \param[in] cloud_tgt the target point cloud dataset - * \param[in] indices_tgt the vector of indices describing the correspondences of the interst points from \a indices_src + * \param[in] indices_tgt the vector of indices describing the correspondences of the interest points from \a indices_src * \param[out] transformation_matrix the resultant transformation matrix */ virtual void diff --git a/registration/include/pcl/registration/transformation_estimation_dq.h b/registration/include/pcl/registration/transformation_estimation_dq.h index 46730ed2..27bad867 100644 --- a/registration/include/pcl/registration/transformation_estimation_dq.h +++ b/registration/include/pcl/registration/transformation_estimation_dq.h @@ -96,7 +96,7 @@ namespace pcl * \param[in] cloud_src the source point cloud dataset * \param[in] indices_src the vector of indices describing the points of interest in \a cloud_src * \param[in] cloud_tgt the target point cloud dataset - * \param[in] indices_tgt the vector of indices describing the correspondences of the interst points from \a indices_src + * \param[in] indices_tgt the vector of indices describing the correspondences of the interest points from \a indices_src * \param[out] transformation_matrix the resultant transformation matrix */ inline void diff --git a/registration/include/pcl/registration/transformation_estimation_dual_quaternion.h b/registration/include/pcl/registration/transformation_estimation_dual_quaternion.h index 6ca1efd8..6d49aaf2 100644 --- a/registration/include/pcl/registration/transformation_estimation_dual_quaternion.h +++ b/registration/include/pcl/registration/transformation_estimation_dual_quaternion.h @@ -96,7 +96,7 @@ namespace pcl * \param[in] cloud_src the source point cloud dataset * \param[in] indices_src the vector of indices describing the points of interest in \a cloud_src * \param[in] cloud_tgt the target point cloud dataset - * \param[in] indices_tgt the vector of indices describing the correspondences of the interst points from \a indices_src + * \param[in] indices_tgt the vector of indices describing the correspondences of the interest points from \a indices_src * \param[out] transformation_matrix the resultant transformation matrix */ inline void diff --git a/registration/include/pcl/registration/transformation_estimation_lm.h b/registration/include/pcl/registration/transformation_estimation_lm.h index 7f3e43bd..601b3fc5 100644 --- a/registration/include/pcl/registration/transformation_estimation_lm.h +++ b/registration/include/pcl/registration/transformation_estimation_lm.h @@ -133,7 +133,7 @@ namespace pcl * \param[in] cloud_src the source point cloud dataset * \param[in] indices_src the vector of indices describing the points of interest in \a cloud_src * \param[in] cloud_tgt the target point cloud dataset - * \param[in] indices_tgt the vector of indices describing the correspondences of the interst points from + * \param[in] indices_tgt the vector of indices describing the correspondences of the interest points from * \a indices_src * \param[out] transformation_matrix the resultant transformation matrix */ @@ -219,7 +219,7 @@ namespace pcl /** Base functor all the models that need non linear optimization must * define their own one and implement operator() (const Eigen::VectorXd& x, Eigen::VectorXd& fvec) - * or operator() (const Eigen::VectorXf& x, Eigen::VectorXf& fvec) dependening on the choosen _Scalar + * or operator() (const Eigen::VectorXf& x, Eigen::VectorXf& fvec) depending on the chosen _Scalar */ template struct Functor @@ -234,7 +234,7 @@ namespace pcl typedef Eigen::Matrix<_Scalar,ValuesAtCompileTime,1> ValueType; typedef Eigen::Matrix<_Scalar,ValuesAtCompileTime,InputsAtCompileTime> JacobianType; - /** \brief Empty Construtor. */ + /** \brief Empty Constructor. */ Functor () : m_data_points_ (ValuesAtCompileTime) {} /** \brief Constructor diff --git a/registration/include/pcl/registration/transformation_estimation_point_to_plane_lls.h b/registration/include/pcl/registration/transformation_estimation_point_to_plane_lls.h index 04d3583f..6b10cde6 100644 --- a/registration/include/pcl/registration/transformation_estimation_point_to_plane_lls.h +++ b/registration/include/pcl/registration/transformation_estimation_point_to_plane_lls.h @@ -99,7 +99,7 @@ namespace pcl * \param[in] cloud_src the source point cloud dataset * \param[in] indices_src the vector of indices describing the points of interest in \a cloud_src * \param[in] cloud_tgt the target point cloud dataset - * \param[in] indices_tgt the vector of indices describing the correspondences of the interst points from \a indices_src + * \param[in] indices_tgt the vector of indices describing the correspondences of the interest points from \a indices_src * \param[out] transformation_matrix the resultant transformation matrix */ inline void @@ -135,7 +135,7 @@ namespace pcl ConstCloudIterator& target_it, Matrix4 &transformation_matrix) const; - /** \brief Construct a 4 by 4 tranformation matrix from the provided rotation and translation. + /** \brief Construct a 4 by 4 transformation matrix from the provided rotation and translation. * \param[in] alpha the rotation about the x-axis * \param[in] beta the rotation about the y-axis * \param[in] gamma the rotation about the z-axis diff --git a/registration/include/pcl/registration/transformation_estimation_point_to_plane_lls_weighted.h b/registration/include/pcl/registration/transformation_estimation_point_to_plane_lls_weighted.h index 33d8866d..6af60f0d 100644 --- a/registration/include/pcl/registration/transformation_estimation_point_to_plane_lls_weighted.h +++ b/registration/include/pcl/registration/transformation_estimation_point_to_plane_lls_weighted.h @@ -99,7 +99,7 @@ namespace pcl * \param[in] cloud_src the source point cloud dataset * \param[in] indices_src the vector of indices describing the points of interest in \a cloud_src * \param[in] cloud_tgt the target point cloud dataset - * \param[in] indices_tgt the vector of indices describing the correspondences of the interst points from \a indices_src + * \param[in] indices_tgt the vector of indices describing the correspondences of the interest points from \a indices_src * \param[out] transformation_matrix the resultant transformation matrix */ inline void @@ -145,7 +145,7 @@ namespace pcl typename std::vector::const_iterator& weights_it, Matrix4 &transformation_matrix) const; - /** \brief Construct a 4 by 4 tranformation matrix from the provided rotation and translation. + /** \brief Construct a 4 by 4 transformation matrix from the provided rotation and translation. * \param[in] alpha the rotation about the x-axis * \param[in] beta the rotation about the y-axis * \param[in] gamma the rotation about the z-axis diff --git a/registration/include/pcl/registration/transformation_estimation_point_to_plane_weighted.h b/registration/include/pcl/registration/transformation_estimation_point_to_plane_weighted.h index 3013d670..42931cb0 100644 --- a/registration/include/pcl/registration/transformation_estimation_point_to_plane_weighted.h +++ b/registration/include/pcl/registration/transformation_estimation_point_to_plane_weighted.h @@ -139,7 +139,7 @@ namespace pcl * \param[in] cloud_src the source point cloud dataset * \param[in] indices_src the vector of indices describing the points of interest in \a cloud_src * \param[in] cloud_tgt the target point cloud dataset - * \param[in] indices_tgt the vector of indices describing the correspondences of the interst points from + * \param[in] indices_tgt the vector of indices describing the correspondences of the interest points from * \a indices_src * \param[out] transformation_matrix the resultant transformation matrix * \note Uses the weights given by setWeights. @@ -204,7 +204,7 @@ namespace pcl /** Base functor all the models that need non linear optimization must * define their own one and implement operator() (const Eigen::VectorXd& x, Eigen::VectorXd& fvec) - * or operator() (const Eigen::VectorXf& x, Eigen::VectorXf& fvec) dependening on the choosen _Scalar + * or operator() (const Eigen::VectorXf& x, Eigen::VectorXf& fvec) depending on the chosen _Scalar */ template struct Functor @@ -219,7 +219,7 @@ namespace pcl typedef Eigen::Matrix<_Scalar,ValuesAtCompileTime,1> ValueType; typedef Eigen::Matrix<_Scalar,ValuesAtCompileTime,InputsAtCompileTime> JacobianType; - /** \brief Empty Construtor. */ + /** \brief Empty Constructor. */ Functor () : m_data_points_ (ValuesAtCompileTime) {} /** \brief Constructor diff --git a/registration/include/pcl/registration/transformation_estimation_svd.h b/registration/include/pcl/registration/transformation_estimation_svd.h index c5030686..ba082c14 100644 --- a/registration/include/pcl/registration/transformation_estimation_svd.h +++ b/registration/include/pcl/registration/transformation_estimation_svd.h @@ -99,7 +99,7 @@ namespace pcl * \param[in] cloud_src the source point cloud dataset * \param[in] indices_src the vector of indices describing the points of interest in \a cloud_src * \param[in] cloud_tgt the target point cloud dataset - * \param[in] indices_tgt the vector of indices describing the correspondences of the interst points from \a indices_src + * \param[in] indices_tgt the vector of indices describing the correspondences of the interest points from \a indices_src * \param[out] transformation_matrix the resultant transformation matrix */ inline void diff --git a/registration/include/pcl/registration/transformation_validation.h b/registration/include/pcl/registration/transformation_validation.h index 9db6d173..41f5628d 100644 --- a/registration/include/pcl/registration/transformation_validation.h +++ b/registration/include/pcl/registration/transformation_validation.h @@ -101,7 +101,7 @@ namespace pcl /** \brief Comparator function for deciding which score is better after running the * validation on multiple transforms. Pure virtual. * - * \note For example, for Euclidean distances smaller is better, for inliers the oposite. + * \note For example, for Euclidean distances smaller is better, for inliers the opposite. * * \param[in] score1 the first value * \param[in] score2 the second value diff --git a/registration/src/gicp6d.cpp b/registration/src/gicp6d.cpp index ef6165b0..3c0f8a28 100644 --- a/registration/src/gicp6d.cpp +++ b/registration/src/gicp6d.cpp @@ -311,7 +311,7 @@ namespace pcl PCL_DEBUG("[pcl::%s::computeTransformation] Convergence failed\n", getClassName ().c_str ()); } - //for some reason the static equivalent methode raises an error + //for some reason the static equivalent method raises an error // final_transformation_.block<3,3> (0,0) = (transformation_.block<3,3> (0,0)) * (guess.block<3,3> (0,0)); // final_transformation_.block <3, 1> (0, 3) = transformation_.block <3, 1> (0, 3) + guess.rightCols<1>.block <3, 1> (0, 3); final_transformation_.topLeftCorner (3, 3) = diff --git a/sample_consensus/include/pcl/sample_consensus/impl/lmeds.hpp b/sample_consensus/include/pcl/sample_consensus/impl/lmeds.hpp index c24e988a..960e3c0b 100644 --- a/sample_consensus/include/pcl/sample_consensus/impl/lmeds.hpp +++ b/sample_consensus/include/pcl/sample_consensus/impl/lmeds.hpp @@ -65,7 +65,7 @@ pcl::LeastMedianSquares::computeModel (int debug_verbosity_level) int n_inliers_count = 0; unsigned skipped_count = 0; - // supress infinite loops by just allowing 10 x maximum allowed iterations for invalid model parameters! + // suppress infinite loops by just allowing 10 x maximum allowed iterations for invalid model parameters! const unsigned max_skip = max_iterations_ * 10; // Iterate diff --git a/sample_consensus/include/pcl/sample_consensus/impl/mlesac.hpp b/sample_consensus/include/pcl/sample_consensus/impl/mlesac.hpp index 7c6141e2..a92cf388 100644 --- a/sample_consensus/include/pcl/sample_consensus/impl/mlesac.hpp +++ b/sample_consensus/include/pcl/sample_consensus/impl/mlesac.hpp @@ -78,7 +78,7 @@ pcl::MaximumLikelihoodSampleConsensus::computeModel (int debug_verbosity int n_inliers_count = 0; size_t indices_size; unsigned skipped_count = 0; - // supress infinite loops by just allowing 10 x maximum allowed iterations for invalid model parameters! + // suppress infinite loops by just allowing 10 x maximum allowed iterations for invalid model parameters! const unsigned max_skip = max_iterations_ * 10; // Iterate diff --git a/sample_consensus/include/pcl/sample_consensus/impl/msac.hpp b/sample_consensus/include/pcl/sample_consensus/impl/msac.hpp index 659d338e..888db53c 100644 --- a/sample_consensus/include/pcl/sample_consensus/impl/msac.hpp +++ b/sample_consensus/include/pcl/sample_consensus/impl/msac.hpp @@ -65,7 +65,7 @@ pcl::MEstimatorSampleConsensus::computeModel (int debug_verbosity_level) int n_inliers_count = 0; unsigned skipped_count = 0; - // supress infinite loops by just allowing 10 x maximum allowed iterations for invalid model parameters! + // suppress infinite loops by just allowing 10 x maximum allowed iterations for invalid model parameters! const unsigned max_skip = max_iterations_ * 10; // Iterate diff --git a/sample_consensus/include/pcl/sample_consensus/impl/ransac.hpp b/sample_consensus/include/pcl/sample_consensus/impl/ransac.hpp index 3c174fea..bab6a1b9 100644 --- a/sample_consensus/include/pcl/sample_consensus/impl/ransac.hpp +++ b/sample_consensus/include/pcl/sample_consensus/impl/ransac.hpp @@ -66,7 +66,7 @@ pcl::RandomSampleConsensus::computeModel (int) int n_inliers_count = 0; unsigned skipped_count = 0; - // supress infinite loops by just allowing 10 x maximum allowed iterations for invalid model parameters! + // suppress infinite loops by just allowing 10 x maximum allowed iterations for invalid model parameters! const unsigned max_skip = max_iterations_ * 10; // Iterate diff --git a/sample_consensus/include/pcl/sample_consensus/impl/rmsac.hpp b/sample_consensus/include/pcl/sample_consensus/impl/rmsac.hpp index 186f3033..4948735c 100644 --- a/sample_consensus/include/pcl/sample_consensus/impl/rmsac.hpp +++ b/sample_consensus/include/pcl/sample_consensus/impl/rmsac.hpp @@ -66,7 +66,7 @@ pcl::RandomizedMEstimatorSampleConsensus::computeModel (int debug_verbos int n_inliers_count = 0; unsigned skipped_count = 0; - // supress infinite loops by just allowing 10 x maximum allowed iterations for invalid model parameters! + // suppress infinite loops by just allowing 10 x maximum allowed iterations for invalid model parameters! const unsigned max_skip = max_iterations_ * 10; // Number of samples to try randomly diff --git a/sample_consensus/include/pcl/sample_consensus/impl/rransac.hpp b/sample_consensus/include/pcl/sample_consensus/impl/rransac.hpp index 338780dc..62aed6f4 100644 --- a/sample_consensus/include/pcl/sample_consensus/impl/rransac.hpp +++ b/sample_consensus/include/pcl/sample_consensus/impl/rransac.hpp @@ -64,7 +64,7 @@ pcl::RandomizedRandomSampleConsensus::computeModel (int debug_verbosity_ int n_inliers_count = 0; unsigned skipped_count = 0; - // supress infinite loops by just allowing 10 x maximum allowed iterations for invalid model parameters! + // suppress infinite loops by just allowing 10 x maximum allowed iterations for invalid model parameters! const unsigned max_skip = max_iterations_ * 10; // Number of samples to try randomly diff --git a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_circle.hpp b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_circle.hpp index 1871cd41..e209e0f1 100644 --- a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_circle.hpp +++ b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_circle.hpp @@ -66,7 +66,7 @@ pcl::SampleConsensusModelCircle2D::isSampleGood(const std::vector & ////////////////////////////////////////////////////////////////////////// template bool -pcl::SampleConsensusModelCircle2D::computeModelCoefficients (const std::vector &samples, Eigen::VectorXf &model_coefficients) +pcl::SampleConsensusModelCircle2D::computeModelCoefficients (const std::vector &samples, Eigen::VectorXf &model_coefficients) const { // Need 3 samples if (samples.size () != 3) @@ -102,7 +102,7 @@ pcl::SampleConsensusModelCircle2D::computeModelCoefficients (const std:: ////////////////////////////////////////////////////////////////////////// template void -pcl::SampleConsensusModelCircle2D::getDistancesToModel (const Eigen::VectorXf &model_coefficients, std::vector &distances) +pcl::SampleConsensusModelCircle2D::getDistancesToModel (const Eigen::VectorXf &model_coefficients, std::vector &distances) const { // Check if the model is valid given the user constraints if (!isModelValid (model_coefficients)) @@ -168,7 +168,7 @@ pcl::SampleConsensusModelCircle2D::selectWithinDistance ( ////////////////////////////////////////////////////////////////////////// template int pcl::SampleConsensusModelCircle2D::countWithinDistance ( - const Eigen::VectorXf &model_coefficients, const double threshold) + const Eigen::VectorXf &model_coefficients, const double threshold) const { // Check if the model is valid given the user constraints if (!isModelValid (model_coefficients)) @@ -196,7 +196,7 @@ pcl::SampleConsensusModelCircle2D::countWithinDistance ( ////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelCircle2D::optimizeModelCoefficients ( - const std::vector &inliers, const Eigen::VectorXf &model_coefficients, Eigen::VectorXf &optimized_coefficients) + const std::vector &inliers, const Eigen::VectorXf &model_coefficients, Eigen::VectorXf &optimized_coefficients) const { optimized_coefficients = model_coefficients; @@ -214,9 +214,7 @@ pcl::SampleConsensusModelCircle2D::optimizeModelCoefficients ( return; } - tmp_inliers_ = &inliers; - - OptimizationFunctor functor (static_cast (inliers.size ()), this); + OptimizationFunctor functor (this, inliers); Eigen::NumericalDiff num_diff (functor); Eigen::LevenbergMarquardt, float> lm (num_diff); int info = lm.minimize (optimized_coefficients); @@ -230,7 +228,7 @@ pcl::SampleConsensusModelCircle2D::optimizeModelCoefficients ( template void pcl::SampleConsensusModelCircle2D::projectPoints ( const std::vector &inliers, const Eigen::VectorXf &model_coefficients, - PointCloud &projected_points, bool copy_data_fields) + PointCloud &projected_points, bool copy_data_fields) const { // Needs a valid set of model coefficients if (model_coefficients.size () != 3) @@ -296,7 +294,7 @@ pcl::SampleConsensusModelCircle2D::projectPoints ( ////////////////////////////////////////////////////////////////////////// template bool pcl::SampleConsensusModelCircle2D::doSamplesVerifyModel ( - const std::set &indices, const Eigen::VectorXf &model_coefficients, const double threshold) + const std::set &indices, const Eigen::VectorXf &model_coefficients, const double threshold) const { // Needs a valid model coefficients if (model_coefficients.size () != 3) @@ -321,7 +319,7 @@ pcl::SampleConsensusModelCircle2D::doSamplesVerifyModel ( ////////////////////////////////////////////////////////////////////////// template bool -pcl::SampleConsensusModelCircle2D::isModelValid (const Eigen::VectorXf &model_coefficients) +pcl::SampleConsensusModelCircle2D::isModelValid (const Eigen::VectorXf &model_coefficients) const { if (!SampleConsensusModel::isModelValid (model_coefficients)) return (false); diff --git a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_circle3d.hpp b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_circle3d.hpp index 6b4bff71..1f255af0 100644 --- a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_circle3d.hpp +++ b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_circle3d.hpp @@ -65,7 +65,7 @@ pcl::SampleConsensusModelCircle3D::isSampleGood ( ////////////////////////////////////////////////////////////////////////// template bool -pcl::SampleConsensusModelCircle3D::computeModelCoefficients (const std::vector &samples, Eigen::VectorXf &model_coefficients) +pcl::SampleConsensusModelCircle3D::computeModelCoefficients (const std::vector &samples, Eigen::VectorXf &model_coefficients) const { // Need 3 samples if (samples.size () != 3) @@ -115,7 +115,7 @@ pcl::SampleConsensusModelCircle3D::computeModelCoefficients (const std:: ////////////////////////////////////////////////////////////////////////// template void -pcl::SampleConsensusModelCircle3D::getDistancesToModel (const Eigen::VectorXf &model_coefficients, std::vector &distances) +pcl::SampleConsensusModelCircle3D::getDistancesToModel (const Eigen::VectorXf &model_coefficients, std::vector &distances) const { // Check if the model is valid given the user constraints if (!isModelValid (model_coefficients)) @@ -212,7 +212,7 @@ pcl::SampleConsensusModelCircle3D::selectWithinDistance ( ////////////////////////////////////////////////////////////////////////// template int pcl::SampleConsensusModelCircle3D::countWithinDistance ( - const Eigen::VectorXf &model_coefficients, const double threshold) + const Eigen::VectorXf &model_coefficients, const double threshold) const { // Check if the model is valid given the user constraints if (!isModelValid (model_coefficients)) @@ -253,9 +253,9 @@ pcl::SampleConsensusModelCircle3D::countWithinDistance ( ////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelCircle3D::optimizeModelCoefficients ( - const std::vector &inliers, - const Eigen::VectorXf &model_coefficients, - Eigen::VectorXf &optimized_coefficients) + const std::vector &inliers, + const Eigen::VectorXf &model_coefficients, + Eigen::VectorXf &optimized_coefficients) const { optimized_coefficients = model_coefficients; @@ -273,9 +273,7 @@ pcl::SampleConsensusModelCircle3D::optimizeModelCoefficients ( return; } - tmp_inliers_ = &inliers; - - OptimizationFunctor functor (static_cast (inliers.size ()), this); + OptimizationFunctor functor (this, inliers); Eigen::NumericalDiff num_diff (functor); Eigen::LevenbergMarquardt, double> lm (num_diff); Eigen::VectorXd coeff; @@ -292,7 +290,7 @@ pcl::SampleConsensusModelCircle3D::optimizeModelCoefficients ( template void pcl::SampleConsensusModelCircle3D::projectPoints ( const std::vector &inliers, const Eigen::VectorXf &model_coefficients, - PointCloud &projected_points, bool copy_data_fields) + PointCloud &projected_points, bool copy_data_fields) const { // Needs a valid set of model coefficients if (model_coefficients.size () != 7) @@ -393,9 +391,9 @@ pcl::SampleConsensusModelCircle3D::projectPoints ( ////////////////////////////////////////////////////////////////////////// template bool pcl::SampleConsensusModelCircle3D::doSamplesVerifyModel ( - const std::set &indices, - const Eigen::VectorXf &model_coefficients, - const double threshold) + const std::set &indices, + const Eigen::VectorXf &model_coefficients, + const double threshold) const { // Needs a valid model coefficients if (model_coefficients.size () != 7) @@ -437,7 +435,7 @@ pcl::SampleConsensusModelCircle3D::doSamplesVerifyModel ( ////////////////////////////////////////////////////////////////////////// template bool -pcl::SampleConsensusModelCircle3D::isModelValid (const Eigen::VectorXf &model_coefficients) +pcl::SampleConsensusModelCircle3D::isModelValid (const Eigen::VectorXf &model_coefficients) const { if (!SampleConsensusModel::isModelValid (model_coefficients)) return (false); diff --git a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_cone.hpp b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_cone.hpp index 0b310515..ad679bbd 100644 --- a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_cone.hpp +++ b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_cone.hpp @@ -53,7 +53,7 @@ pcl::SampleConsensusModelCone::isSampleGood(const std::vector bool pcl::SampleConsensusModelCone::computeModelCoefficients ( - const std::vector &samples, Eigen::VectorXf &model_coefficients) + const std::vector &samples, Eigen::VectorXf &model_coefficients) const { // Need 3 samples if (samples.size () != 3) @@ -135,7 +135,7 @@ pcl::SampleConsensusModelCone::computeModelCoefficients ( ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelCone::getDistancesToModel ( - const Eigen::VectorXf &model_coefficients, std::vector &distances) + const Eigen::VectorXf &model_coefficients, std::vector &distances) const { // Check if the model is valid given the user constraints if (!isModelValid (model_coefficients)) @@ -172,7 +172,7 @@ pcl::SampleConsensusModelCone::getDistancesToModel ( // Calculate the cones perfect normals Eigen::Vector4f cone_normal = sinf (opening_angle) * height + cosf (opening_angle) * dir; - // Aproximate the distance from the point to the cone as the difference between + // Approximate the distance from the point to the cone as the difference between // dist(point,cone_axis) and actual cone radius double d_euclid = fabs (pointToAxisDistance (pt, model_coefficients) - actual_cone_radius); @@ -228,7 +228,7 @@ pcl::SampleConsensusModelCone::selectWithinDistance ( // Calculate the cones perfect normals Eigen::Vector4f cone_normal = sinf (opening_angle) * height + cosf (opening_angle) * pp_pt_dir; - // Aproximate the distance from the point to the cone as the difference between + // Approximate the distance from the point to the cone as the difference between // dist(point,cone_axis) and actual cone radius double d_euclid = fabs (pointToAxisDistance (pt, model_coefficients) - actual_cone_radius); @@ -253,7 +253,7 @@ pcl::SampleConsensusModelCone::selectWithinDistance ( ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template int pcl::SampleConsensusModelCone::countWithinDistance ( - const Eigen::VectorXf &model_coefficients, const double threshold) + const Eigen::VectorXf &model_coefficients, const double threshold) const { // Check if the model is valid given the user constraints @@ -290,7 +290,7 @@ pcl::SampleConsensusModelCone::countWithinDistance ( // Calculate the cones perfect normals Eigen::Vector4f cone_normal = sinf (opening_angle) * height + cosf (opening_angle) * pp_pt_dir; - // Aproximate the distance from the point to the cone as the difference between + // Approximate the distance from the point to the cone as the difference between // dist(point,cone_axis) and actual cone radius double d_euclid = fabs (pointToAxisDistance (pt, model_coefficients) - actual_cone_radius); @@ -307,7 +307,7 @@ pcl::SampleConsensusModelCone::countWithinDistance ( ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelCone::optimizeModelCoefficients ( - const std::vector &inliers, const Eigen::VectorXf &model_coefficients, Eigen::VectorXf &optimized_coefficients) + const std::vector &inliers, const Eigen::VectorXf &model_coefficients, Eigen::VectorXf &optimized_coefficients) const { optimized_coefficients = model_coefficients; @@ -324,9 +324,7 @@ pcl::SampleConsensusModelCone::optimizeModelCoefficients ( return; } - tmp_inliers_ = &inliers; - - OptimizationFunctor functor (static_cast (inliers.size ()), this); + OptimizationFunctor functor (this, inliers); Eigen::NumericalDiff num_diff (functor); Eigen::LevenbergMarquardt, float> lm (num_diff); int info = lm.minimize (optimized_coefficients); @@ -346,7 +344,7 @@ pcl::SampleConsensusModelCone::optimizeModelCoefficients ( ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelCone::projectPoints ( - const std::vector &inliers, const Eigen::VectorXf &model_coefficients, PointCloud &projected_points, bool copy_data_fields) + const std::vector &inliers, const Eigen::VectorXf &model_coefficients, PointCloud &projected_points, bool copy_data_fields) const { // Needs a valid set of model coefficients if (model_coefficients.size () != 7) @@ -442,7 +440,7 @@ pcl::SampleConsensusModelCone::projectPoints ( ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template bool pcl::SampleConsensusModelCone::doSamplesVerifyModel ( - const std::set &indices, const Eigen::VectorXf &model_coefficients, const double threshold) + const std::set &indices, const Eigen::VectorXf &model_coefficients, const double threshold) const { // Needs a valid model coefficients if (model_coefficients.size () != 7) @@ -473,7 +471,7 @@ pcl::SampleConsensusModelCone::doSamplesVerifyModel ( Eigen::Vector4f height = apex - pt_proj; double actual_cone_radius = tan (openning_angle) * height.norm (); - // Aproximate the distance from the point to the cone as the difference between + // Approximate the distance from the point to the cone as the difference between // dist(point,cone_axis) and actual cone radius if (fabs (static_cast(pointToAxisDistance (pt, model_coefficients) - actual_cone_radius)) > threshold) return (false); @@ -485,7 +483,7 @@ pcl::SampleConsensusModelCone::doSamplesVerifyModel ( ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template double pcl::SampleConsensusModelCone::pointToAxisDistance ( - const Eigen::Vector4f &pt, const Eigen::VectorXf &model_coefficients) + const Eigen::Vector4f &pt, const Eigen::VectorXf &model_coefficients) const { Eigen::Vector4f apex (model_coefficients[0], model_coefficients[1], model_coefficients[2], 0); Eigen::Vector4f axis_dir (model_coefficients[3], model_coefficients[4], model_coefficients[5], 0); @@ -494,7 +492,7 @@ pcl::SampleConsensusModelCone::pointToAxisDistance ( ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template bool -pcl::SampleConsensusModelCone::isModelValid (const Eigen::VectorXf &model_coefficients) +pcl::SampleConsensusModelCone::isModelValid (const Eigen::VectorXf &model_coefficients) const { if (!SampleConsensusModel::isModelValid (model_coefficients)) return (false); diff --git a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_cylinder.hpp b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_cylinder.hpp index 190115b2..bc2dcebe 100644 --- a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_cylinder.hpp +++ b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_cylinder.hpp @@ -55,7 +55,7 @@ pcl::SampleConsensusModelCylinder::isSampleGood (const std::vec ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template bool pcl::SampleConsensusModelCylinder::computeModelCoefficients ( - const std::vector &samples, Eigen::VectorXf &model_coefficients) + const std::vector &samples, Eigen::VectorXf &model_coefficients) const { // Need 2 samples if (samples.size () != 2) @@ -129,7 +129,7 @@ pcl::SampleConsensusModelCylinder::computeModelCoefficients ( ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelCylinder::getDistancesToModel ( - const Eigen::VectorXf &model_coefficients, std::vector &distances) + const Eigen::VectorXf &model_coefficients, std::vector &distances) const { // Check if the model is valid given the user constraints if (!isModelValid (model_coefficients)) @@ -147,7 +147,7 @@ pcl::SampleConsensusModelCylinder::getDistancesToModel ( // Iterate through the 3d points and calculate the distances from them to the sphere for (size_t i = 0; i < indices_->size (); ++i) { - // Aproximate the distance from the point to the cylinder as the difference between + // Approximate the distance from the point to the cylinder as the difference between // dist(point,cylinder_axis) and cylinder radius // @note need to revise this. Eigen::Vector4f pt (input_->points[(*indices_)[i]].x, input_->points[(*indices_)[i]].y, input_->points[(*indices_)[i]].z, 0); @@ -192,7 +192,7 @@ pcl::SampleConsensusModelCylinder::selectWithinDistance ( // Iterate through the 3d points and calculate the distances from them to the sphere for (size_t i = 0; i < indices_->size (); ++i) { - // Aproximate the distance from the point to the cylinder as the difference between + // Approximate the distance from the point to the cylinder as the difference between // dist(point,cylinder_axis) and cylinder radius Eigen::Vector4f pt (input_->points[(*indices_)[i]].x, input_->points[(*indices_)[i]].y, input_->points[(*indices_)[i]].z, 0); Eigen::Vector4f n (normals_->points[(*indices_)[i]].normal[0], normals_->points[(*indices_)[i]].normal[1], normals_->points[(*indices_)[i]].normal[2], 0); @@ -224,7 +224,7 @@ pcl::SampleConsensusModelCylinder::selectWithinDistance ( ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template int pcl::SampleConsensusModelCylinder::countWithinDistance ( - const Eigen::VectorXf &model_coefficients, const double threshold) + const Eigen::VectorXf &model_coefficients, const double threshold) const { // Check if the model is valid given the user constraints if (!isModelValid (model_coefficients)) @@ -239,7 +239,7 @@ pcl::SampleConsensusModelCylinder::countWithinDistance ( // Iterate through the 3d points and calculate the distances from them to the sphere for (size_t i = 0; i < indices_->size (); ++i) { - // Aproximate the distance from the point to the cylinder as the difference between + // Approximate the distance from the point to the cylinder as the difference between // dist(point,cylinder_axis) and cylinder radius Eigen::Vector4f pt (input_->points[(*indices_)[i]].x, input_->points[(*indices_)[i]].y, input_->points[(*indices_)[i]].z, 0); Eigen::Vector4f n (normals_->points[(*indices_)[i]].normal[0], normals_->points[(*indices_)[i]].normal[1], normals_->points[(*indices_)[i]].normal[2], 0); @@ -264,7 +264,7 @@ pcl::SampleConsensusModelCylinder::countWithinDistance ( ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelCylinder::optimizeModelCoefficients ( - const std::vector &inliers, const Eigen::VectorXf &model_coefficients, Eigen::VectorXf &optimized_coefficients) + const std::vector &inliers, const Eigen::VectorXf &model_coefficients, Eigen::VectorXf &optimized_coefficients) const { optimized_coefficients = model_coefficients; @@ -281,9 +281,7 @@ pcl::SampleConsensusModelCylinder::optimizeModelCoefficients ( return; } - tmp_inliers_ = &inliers; - - OptimizationFunctor functor (static_cast (inliers.size ()), this); + OptimizationFunctor functor (this, inliers); Eigen::NumericalDiff num_diff (functor); Eigen::LevenbergMarquardt, float> lm (num_diff); int info = lm.minimize (optimized_coefficients); @@ -303,7 +301,7 @@ pcl::SampleConsensusModelCylinder::optimizeModelCoefficients ( ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelCylinder::projectPoints ( - const std::vector &inliers, const Eigen::VectorXf &model_coefficients, PointCloud &projected_points, bool copy_data_fields) + const std::vector &inliers, const Eigen::VectorXf &model_coefficients, PointCloud &projected_points, bool copy_data_fields) const { // Needs a valid set of model coefficients if (model_coefficients.size () != 7) @@ -389,7 +387,7 @@ pcl::SampleConsensusModelCylinder::projectPoints ( ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template bool pcl::SampleConsensusModelCylinder::doSamplesVerifyModel ( - const std::set &indices, const Eigen::VectorXf &model_coefficients, const double threshold) + const std::set &indices, const Eigen::VectorXf &model_coefficients, const double threshold) const { // Needs a valid model coefficients if (model_coefficients.size () != 7) @@ -400,7 +398,7 @@ pcl::SampleConsensusModelCylinder::doSamplesVerifyModel ( for (std::set::const_iterator it = indices.begin (); it != indices.end (); ++it) { - // Aproximate the distance from the point to the cylinder as the difference between + // Approximate the distance from the point to the cylinder as the difference between // dist(point,cylinder_axis) and cylinder radius // @note need to revise this. Eigen::Vector4f pt (input_->points[*it].x, input_->points[*it].y, input_->points[*it].z, 0); @@ -414,7 +412,7 @@ pcl::SampleConsensusModelCylinder::doSamplesVerifyModel ( ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template double pcl::SampleConsensusModelCylinder::pointToLineDistance ( - const Eigen::Vector4f &pt, const Eigen::VectorXf &model_coefficients) + const Eigen::Vector4f &pt, const Eigen::VectorXf &model_coefficients) const { Eigen::Vector4f line_pt (model_coefficients[0], model_coefficients[1], model_coefficients[2], 0); Eigen::Vector4f line_dir (model_coefficients[3], model_coefficients[4], model_coefficients[5], 0); @@ -424,7 +422,7 @@ pcl::SampleConsensusModelCylinder::pointToLineDistance ( ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelCylinder::projectPointToCylinder ( - const Eigen::Vector4f &pt, const Eigen::VectorXf &model_coefficients, Eigen::Vector4f &pt_proj) + const Eigen::Vector4f &pt, const Eigen::VectorXf &model_coefficients, Eigen::Vector4f &pt_proj) const { Eigen::Vector4f line_pt (model_coefficients[0], model_coefficients[1], model_coefficients[2], 0); Eigen::Vector4f line_dir (model_coefficients[3], model_coefficients[4], model_coefficients[5], 0); @@ -441,7 +439,7 @@ pcl::SampleConsensusModelCylinder::projectPointToCylinder ( ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template bool -pcl::SampleConsensusModelCylinder::isModelValid (const Eigen::VectorXf &model_coefficients) +pcl::SampleConsensusModelCylinder::isModelValid (const Eigen::VectorXf &model_coefficients) const { if (!SampleConsensusModel::isModelValid (model_coefficients)) return (false); diff --git a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_line.hpp b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_line.hpp index af019a1a..7c7fd751 100644 --- a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_line.hpp +++ b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_line.hpp @@ -57,13 +57,13 @@ pcl::SampleConsensusModelLine::isSampleGood (const std::vector &sam (input_->points[samples[0]].z != input_->points[samples[1]].z)) return (true); - return (true); + return (false); } ////////////////////////////////////////////////////////////////////////// template bool pcl::SampleConsensusModelLine::computeModelCoefficients ( - const std::vector &samples, Eigen::VectorXf &model_coefficients) + const std::vector &samples, Eigen::VectorXf &model_coefficients) const { // Need 2 samples if (samples.size () != 2) @@ -95,7 +95,7 @@ pcl::SampleConsensusModelLine::computeModelCoefficients ( ////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelLine::getDistancesToModel ( - const Eigen::VectorXf &model_coefficients, std::vector &distances) + const Eigen::VectorXf &model_coefficients, std::vector &distances) const { // Needs a valid set of model coefficients if (!isModelValid (model_coefficients)) @@ -160,7 +160,7 @@ pcl::SampleConsensusModelLine::selectWithinDistance ( ////////////////////////////////////////////////////////////////////////// template int pcl::SampleConsensusModelLine::countWithinDistance ( - const Eigen::VectorXf &model_coefficients, const double threshold) + const Eigen::VectorXf &model_coefficients, const double threshold) const { // Needs a valid set of model coefficients if (!isModelValid (model_coefficients)) @@ -191,7 +191,7 @@ pcl::SampleConsensusModelLine::countWithinDistance ( ////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelLine::optimizeModelCoefficients ( - const std::vector &inliers, const Eigen::VectorXf &model_coefficients, Eigen::VectorXf &optimized_coefficients) + const std::vector &inliers, const Eigen::VectorXf &model_coefficients, Eigen::VectorXf &optimized_coefficients) const { // Needs a valid set of model coefficients if (!isModelValid (model_coefficients)) @@ -232,7 +232,7 @@ pcl::SampleConsensusModelLine::optimizeModelCoefficients ( ////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelLine::projectPoints ( - const std::vector &inliers, const Eigen::VectorXf &model_coefficients, PointCloud &projected_points, bool copy_data_fields) + const std::vector &inliers, const Eigen::VectorXf &model_coefficients, PointCloud &projected_points, bool copy_data_fields) const { // Needs a valid model coefficients if (!isModelValid (model_coefficients)) @@ -305,7 +305,7 @@ pcl::SampleConsensusModelLine::projectPoints ( ////////////////////////////////////////////////////////////////////////// template bool pcl::SampleConsensusModelLine::doSamplesVerifyModel ( - const std::set &indices, const Eigen::VectorXf &model_coefficients, const double threshold) + const std::set &indices, const Eigen::VectorXf &model_coefficients, const double threshold) const { // Needs a valid set of model coefficients if (!isModelValid (model_coefficients)) diff --git a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_normal_parallel_plane.hpp b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_normal_parallel_plane.hpp index 55ce7d7f..d754dc94 100644 --- a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_normal_parallel_plane.hpp +++ b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_normal_parallel_plane.hpp @@ -45,7 +45,7 @@ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template bool -pcl::SampleConsensusModelNormalParallelPlane::isModelValid (const Eigen::VectorXf &model_coefficients) +pcl::SampleConsensusModelNormalParallelPlane::isModelValid (const Eigen::VectorXf &model_coefficients) const { if (!SampleConsensusModel::isModelValid (model_coefficients)) return (false); diff --git a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_normal_plane.hpp b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_normal_plane.hpp index f56d1c02..7dfbc08f 100644 --- a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_normal_plane.hpp +++ b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_normal_plane.hpp @@ -104,7 +104,7 @@ pcl::SampleConsensusModelNormalPlane::selectWithinDistance ( ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template int pcl::SampleConsensusModelNormalPlane::countWithinDistance ( - const Eigen::VectorXf &model_coefficients, const double threshold) + const Eigen::VectorXf &model_coefficients, const double threshold) const { if (!normals_) { @@ -149,7 +149,7 @@ pcl::SampleConsensusModelNormalPlane::countWithinDistance ( ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelNormalPlane::getDistancesToModel ( - const Eigen::VectorXf &model_coefficients, std::vector &distances) + const Eigen::VectorXf &model_coefficients, std::vector &distances) const { if (!normals_) { diff --git a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_normal_sphere.hpp b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_normal_sphere.hpp index 3169f38e..02cdde31 100644 --- a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_normal_sphere.hpp +++ b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_normal_sphere.hpp @@ -108,7 +108,7 @@ pcl::SampleConsensusModelNormalSphere::selectWithinDistance ( ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template int pcl::SampleConsensusModelNormalSphere::countWithinDistance ( - const Eigen::VectorXf &model_coefficients, const double threshold) + const Eigen::VectorXf &model_coefficients, const double threshold) const { if (!normals_) { @@ -158,7 +158,7 @@ pcl::SampleConsensusModelNormalSphere::countWithinDistance ( ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelNormalSphere::getDistancesToModel ( - const Eigen::VectorXf &model_coefficients, std::vector &distances) + const Eigen::VectorXf &model_coefficients, std::vector &distances) const { if (!normals_) { @@ -207,7 +207,7 @@ pcl::SampleConsensusModelNormalSphere::getDistancesToModel ( ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template bool -pcl::SampleConsensusModelNormalSphere::isModelValid (const Eigen::VectorXf &model_coefficients) +pcl::SampleConsensusModelNormalSphere::isModelValid (const Eigen::VectorXf &model_coefficients) const { if (!SampleConsensusModel::isModelValid (model_coefficients)) return (false); diff --git a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_parallel_line.hpp b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_parallel_line.hpp index 366aaf82..6426f6fd 100644 --- a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_parallel_line.hpp +++ b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_parallel_line.hpp @@ -62,7 +62,7 @@ pcl::SampleConsensusModelParallelLine::selectWithinDistance ( ////////////////////////////////////////////////////////////////////////// template int pcl::SampleConsensusModelParallelLine::countWithinDistance ( - const Eigen::VectorXf &model_coefficients, const double threshold) + const Eigen::VectorXf &model_coefficients, const double threshold) const { // Check if the model is valid given the user constraints if (!isModelValid (model_coefficients)) @@ -74,7 +74,7 @@ pcl::SampleConsensusModelParallelLine::countWithinDistance ( ////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelParallelLine::getDistancesToModel ( - const Eigen::VectorXf &model_coefficients, std::vector &distances) + const Eigen::VectorXf &model_coefficients, std::vector &distances) const { // Check if the model is valid given the user constraints if (!isModelValid (model_coefficients)) @@ -88,7 +88,7 @@ pcl::SampleConsensusModelParallelLine::getDistancesToModel ( ////////////////////////////////////////////////////////////////////////// template bool -pcl::SampleConsensusModelParallelLine::isModelValid (const Eigen::VectorXf &model_coefficients) +pcl::SampleConsensusModelParallelLine::isModelValid (const Eigen::VectorXf &model_coefficients) const { if (!SampleConsensusModel::isModelValid (model_coefficients)) return (false); diff --git a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_parallel_plane.hpp b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_parallel_plane.hpp index 9db4c1b6..c9dc36ea 100644 --- a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_parallel_plane.hpp +++ b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_parallel_plane.hpp @@ -61,7 +61,7 @@ pcl::SampleConsensusModelParallelPlane::selectWithinDistance ( ////////////////////////////////////////////////////////////////////////// template int pcl::SampleConsensusModelParallelPlane::countWithinDistance ( - const Eigen::VectorXf &model_coefficients, const double threshold) + const Eigen::VectorXf &model_coefficients, const double threshold) const { // Check if the model is valid given the user constraints if (!isModelValid (model_coefficients)) @@ -73,7 +73,7 @@ pcl::SampleConsensusModelParallelPlane::countWithinDistance ( ////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelParallelPlane::getDistancesToModel ( - const Eigen::VectorXf &model_coefficients, std::vector &distances) + const Eigen::VectorXf &model_coefficients, std::vector &distances) const { // Check if the model is valid given the user constraints if (!isModelValid (model_coefficients)) @@ -87,7 +87,7 @@ pcl::SampleConsensusModelParallelPlane::getDistancesToModel ( ////////////////////////////////////////////////////////////////////////// template bool -pcl::SampleConsensusModelParallelPlane::isModelValid (const Eigen::VectorXf &model_coefficients) +pcl::SampleConsensusModelParallelPlane::isModelValid (const Eigen::VectorXf &model_coefficients) const { if (!SampleConsensusModel::isModelValid (model_coefficients)) return (false); diff --git a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_perpendicular_plane.hpp b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_perpendicular_plane.hpp index b080e286..036b7604 100644 --- a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_perpendicular_plane.hpp +++ b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_perpendicular_plane.hpp @@ -61,7 +61,7 @@ pcl::SampleConsensusModelPerpendicularPlane::selectWithinDistance ( ////////////////////////////////////////////////////////////////////////// template int pcl::SampleConsensusModelPerpendicularPlane::countWithinDistance ( - const Eigen::VectorXf &model_coefficients, const double threshold) + const Eigen::VectorXf &model_coefficients, const double threshold) const { // Check if the model is valid given the user constraints if (!isModelValid (model_coefficients)) @@ -73,7 +73,7 @@ pcl::SampleConsensusModelPerpendicularPlane::countWithinDistance ( ////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelPerpendicularPlane::getDistancesToModel ( - const Eigen::VectorXf &model_coefficients, std::vector &distances) + const Eigen::VectorXf &model_coefficients, std::vector &distances) const { // Check if the model is valid given the user constraints if (!isModelValid (model_coefficients)) @@ -87,7 +87,7 @@ pcl::SampleConsensusModelPerpendicularPlane::getDistancesToModel ( ////////////////////////////////////////////////////////////////////////// template bool -pcl::SampleConsensusModelPerpendicularPlane::isModelValid (const Eigen::VectorXf &model_coefficients) +pcl::SampleConsensusModelPerpendicularPlane::isModelValid (const Eigen::VectorXf &model_coefficients) const { if (!SampleConsensusModel::isModelValid (model_coefficients)) return (false); diff --git a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_plane.hpp b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_plane.hpp index 17ec4220..18ad459b 100644 --- a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_plane.hpp +++ b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_plane.hpp @@ -67,7 +67,7 @@ pcl::SampleConsensusModelPlane::isSampleGood (const std::vector &sa ////////////////////////////////////////////////////////////////////////// template bool pcl::SampleConsensusModelPlane::computeModelCoefficients ( - const std::vector &samples, Eigen::VectorXf &model_coefficients) + const std::vector &samples, Eigen::VectorXf &model_coefficients) const { // Need 3 samples if (samples.size () != sample_size_) @@ -110,7 +110,7 @@ pcl::SampleConsensusModelPlane::computeModelCoefficients ( ////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelPlane::getDistancesToModel ( - const Eigen::VectorXf &model_coefficients, std::vector &distances) + const Eigen::VectorXf &model_coefficients, std::vector &distances) const { // Needs a valid set of model coefficients if (model_coefficients.size () != model_size_) @@ -181,7 +181,7 @@ pcl::SampleConsensusModelPlane::selectWithinDistance ( ////////////////////////////////////////////////////////////////////////// template int pcl::SampleConsensusModelPlane::countWithinDistance ( - const Eigen::VectorXf &model_coefficients, const double threshold) + const Eigen::VectorXf &model_coefficients, const double threshold) const { // Needs a valid set of model coefficients if (model_coefficients.size () != model_size_) @@ -210,7 +210,7 @@ pcl::SampleConsensusModelPlane::countWithinDistance ( ////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelPlane::optimizeModelCoefficients ( - const std::vector &inliers, const Eigen::VectorXf &model_coefficients, Eigen::VectorXf &optimized_coefficients) + const std::vector &inliers, const Eigen::VectorXf &model_coefficients, Eigen::VectorXf &optimized_coefficients) const { // Needs a valid set of model coefficients if (model_coefficients.size () != model_size_) @@ -259,7 +259,7 @@ pcl::SampleConsensusModelPlane::optimizeModelCoefficients ( ////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelPlane::projectPoints ( - const std::vector &inliers, const Eigen::VectorXf &model_coefficients, PointCloud &projected_points, bool copy_data_fields) + const std::vector &inliers, const Eigen::VectorXf &model_coefficients, PointCloud &projected_points, bool copy_data_fields) const { // Needs a valid set of model coefficients if (model_coefficients.size () != model_size_) @@ -343,7 +343,7 @@ pcl::SampleConsensusModelPlane::projectPoints ( ////////////////////////////////////////////////////////////////////////// template bool pcl::SampleConsensusModelPlane::doSamplesVerifyModel ( - const std::set &indices, const Eigen::VectorXf &model_coefficients, const double threshold) + const std::set &indices, const Eigen::VectorXf &model_coefficients, const double threshold) const { // Needs a valid set of model coefficients if (model_coefficients.size () != model_size_) diff --git a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_registration.hpp b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_registration.hpp index 6f2de207..9b214ecf 100644 --- a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_registration.hpp +++ b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_registration.hpp @@ -64,7 +64,7 @@ pcl::SampleConsensusModelRegistration::isSampleGood (const std::vector bool -pcl::SampleConsensusModelRegistration::computeModelCoefficients (const std::vector &samples, Eigen::VectorXf &model_coefficients) +pcl::SampleConsensusModelRegistration::computeModelCoefficients (const std::vector &samples, Eigen::VectorXf &model_coefficients) const { if (!target_) { @@ -77,7 +77,7 @@ pcl::SampleConsensusModelRegistration::computeModelCoefficients (const s std::vector indices_tgt (3); for (int i = 0; i < 3; ++i) - indices_tgt[i] = correspondences_[samples[i]]; + indices_tgt[i] = correspondences_.at (samples[i]); estimateRigidTransformationSVD (*input_, samples, *target_, indices_tgt, model_coefficients); return (true); @@ -85,7 +85,7 @@ pcl::SampleConsensusModelRegistration::computeModelCoefficients (const s ////////////////////////////////////////////////////////////////////////// template void -pcl::SampleConsensusModelRegistration::getDistancesToModel (const Eigen::VectorXf &model_coefficients, std::vector &distances) +pcl::SampleConsensusModelRegistration::getDistancesToModel (const Eigen::VectorXf &model_coefficients, std::vector &distances) const { if (indices_->size () != indices_tgt_->size ()) { @@ -191,7 +191,7 @@ pcl::SampleConsensusModelRegistration::selectWithinDistance (const Eigen ////////////////////////////////////////////////////////////////////////// template int pcl::SampleConsensusModelRegistration::countWithinDistance ( - const Eigen::VectorXf &model_coefficients, const double threshold) + const Eigen::VectorXf &model_coefficients, const double threshold) const { if (indices_->size () != indices_tgt_->size ()) { @@ -236,7 +236,7 @@ pcl::SampleConsensusModelRegistration::countWithinDistance ( ////////////////////////////////////////////////////////////////////////// template void -pcl::SampleConsensusModelRegistration::optimizeModelCoefficients (const std::vector &inliers, const Eigen::VectorXf &model_coefficients, Eigen::VectorXf &optimized_coefficients) +pcl::SampleConsensusModelRegistration::optimizeModelCoefficients (const std::vector &inliers, const Eigen::VectorXf &model_coefficients, Eigen::VectorXf &optimized_coefficients) const { if (indices_->size () != indices_tgt_->size ()) { @@ -257,7 +257,7 @@ pcl::SampleConsensusModelRegistration::optimizeModelCoefficients (const for (size_t i = 0; i < inliers.size (); ++i) { indices_src[i] = inliers[i]; - indices_tgt[i] = correspondences_[indices_src[i]]; + indices_tgt[i] = correspondences_.at (indices_src[i]); } estimateRigidTransformationSVD (*input_, indices_src, *target_, indices_tgt, optimized_coefficients); @@ -266,11 +266,11 @@ pcl::SampleConsensusModelRegistration::optimizeModelCoefficients (const ////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelRegistration::estimateRigidTransformationSVD ( - const pcl::PointCloud &cloud_src, - const std::vector &indices_src, + const pcl::PointCloud &cloud_src, + const std::vector &indices_src, const pcl::PointCloud &cloud_tgt, - const std::vector &indices_tgt, - Eigen::VectorXf &transform) + const std::vector &indices_tgt, + Eigen::VectorXf &transform) const { transform.resize (16); diff --git a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_registration_2d.hpp b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_registration_2d.hpp index 54b8688e..afc6b93b 100644 --- a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_registration_2d.hpp +++ b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_registration_2d.hpp @@ -61,7 +61,7 @@ pcl::SampleConsensusModelRegistration2D::isSampleGood (const std::vector ////////////////////////////////////////////////////////////////////////// template void -pcl::SampleConsensusModelRegistration2D::getDistancesToModel (const Eigen::VectorXf &model_coefficients, std::vector &distances) +pcl::SampleConsensusModelRegistration2D::getDistancesToModel (const Eigen::VectorXf &model_coefficients, std::vector &distances) const { PCL_INFO ("[pcl::SampleConsensusModelRegistration2D::getDistancesToModel]\n"); if (indices_->size () != indices_tgt_->size ()) @@ -176,7 +176,7 @@ pcl::SampleConsensusModelRegistration2D::selectWithinDistance (const Eig ////////////////////////////////////////////////////////////////////////// template int pcl::SampleConsensusModelRegistration2D::countWithinDistance ( - const Eigen::VectorXf &model_coefficients, const double threshold) + const Eigen::VectorXf &model_coefficients, const double threshold) const { if (indices_->size () != indices_tgt_->size ()) { diff --git a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_sphere.hpp b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_sphere.hpp index f0791721..604b42be 100644 --- a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_sphere.hpp +++ b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_sphere.hpp @@ -54,7 +54,7 @@ pcl::SampleConsensusModelSphere::isSampleGood (const std::vector &) ////////////////////////////////////////////////////////////////////////// template bool pcl::SampleConsensusModelSphere::computeModelCoefficients ( - const std::vector &samples, Eigen::VectorXf &model_coefficients) + const std::vector &samples, Eigen::VectorXf &model_coefficients) const { // Need 4 samples if (samples.size () != 4) @@ -120,7 +120,7 @@ pcl::SampleConsensusModelSphere::computeModelCoefficients ( ////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelSphere::getDistancesToModel ( - const Eigen::VectorXf &model_coefficients, std::vector &distances) + const Eigen::VectorXf &model_coefficients, std::vector &distances) const { // Check if the model is valid given the user constraints if (!isModelValid (model_coefficients)) @@ -192,7 +192,7 @@ pcl::SampleConsensusModelSphere::selectWithinDistance ( ////////////////////////////////////////////////////////////////////////// template int pcl::SampleConsensusModelSphere::countWithinDistance ( - const Eigen::VectorXf &model_coefficients, const double threshold) + const Eigen::VectorXf &model_coefficients, const double threshold) const { // Check if the model is valid given the user constraints if (!isModelValid (model_coefficients)) @@ -223,7 +223,7 @@ pcl::SampleConsensusModelSphere::countWithinDistance ( ////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelSphere::optimizeModelCoefficients ( - const std::vector &inliers, const Eigen::VectorXf &model_coefficients, Eigen::VectorXf &optimized_coefficients) + const std::vector &inliers, const Eigen::VectorXf &model_coefficients, Eigen::VectorXf &optimized_coefficients) const { optimized_coefficients = model_coefficients; @@ -241,9 +241,7 @@ pcl::SampleConsensusModelSphere::optimizeModelCoefficients ( return; } - tmp_inliers_ = &inliers; - - OptimizationFunctor functor (static_cast (inliers.size ()), this); + OptimizationFunctor functor (this, inliers); Eigen::NumericalDiff num_diff (functor); Eigen::LevenbergMarquardt, float> lm (num_diff); int info = lm.minimize (optimized_coefficients); @@ -256,7 +254,7 @@ pcl::SampleConsensusModelSphere::optimizeModelCoefficients ( ////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelSphere::projectPoints ( - const std::vector &, const Eigen::VectorXf &model_coefficients, PointCloud &projected_points, bool) + const std::vector &, const Eigen::VectorXf &model_coefficients, PointCloud &projected_points, bool) const { // Needs a valid model coefficients if (model_coefficients.size () != 4) @@ -279,7 +277,7 @@ pcl::SampleConsensusModelSphere::projectPoints ( ////////////////////////////////////////////////////////////////////////// template bool pcl::SampleConsensusModelSphere::doSamplesVerifyModel ( - const std::set &indices, const Eigen::VectorXf &model_coefficients, const double threshold) + const std::set &indices, const Eigen::VectorXf &model_coefficients, const double threshold) const { // Needs a valid model coefficients if (model_coefficients.size () != 4) diff --git a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_stick.hpp b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_stick.hpp index 3e43cded..ebfe50d3 100644 --- a/sample_consensus/include/pcl/sample_consensus/impl/sac_model_stick.hpp +++ b/sample_consensus/include/pcl/sample_consensus/impl/sac_model_stick.hpp @@ -63,7 +63,7 @@ pcl::SampleConsensusModelStick::isSampleGood (const std::vector &sa ////////////////////////////////////////////////////////////////////////// template bool pcl::SampleConsensusModelStick::computeModelCoefficients ( - const std::vector &samples, Eigen::VectorXf &model_coefficients) + const std::vector &samples, Eigen::VectorXf &model_coefficients) const { // Need 2 samples if (samples.size () != 2) @@ -94,7 +94,7 @@ pcl::SampleConsensusModelStick::computeModelCoefficients ( ////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelStick::getDistancesToModel ( - const Eigen::VectorXf &model_coefficients, std::vector &distances) + const Eigen::VectorXf &model_coefficients, std::vector &distances) const { // Needs a valid set of model coefficients if (!isModelValid (model_coefficients)) @@ -176,7 +176,7 @@ pcl::SampleConsensusModelStick::selectWithinDistance ( /////////////////////////////////////////////////////////////////////////// template int pcl::SampleConsensusModelStick::countWithinDistance ( - const Eigen::VectorXf &model_coefficients, const double threshold) + const Eigen::VectorXf &model_coefficients, const double threshold) const { // Needs a valid set of model coefficients if (!isModelValid (model_coefficients)) @@ -221,7 +221,7 @@ pcl::SampleConsensusModelStick::countWithinDistance ( ////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelStick::optimizeModelCoefficients ( - const std::vector &inliers, const Eigen::VectorXf &model_coefficients, Eigen::VectorXf &optimized_coefficients) + const std::vector &inliers, const Eigen::VectorXf &model_coefficients, Eigen::VectorXf &optimized_coefficients) const { // Needs a valid set of model coefficients if (!isModelValid (model_coefficients)) @@ -262,7 +262,7 @@ pcl::SampleConsensusModelStick::optimizeModelCoefficients ( ////////////////////////////////////////////////////////////////////////// template void pcl::SampleConsensusModelStick::projectPoints ( - const std::vector &inliers, const Eigen::VectorXf &model_coefficients, PointCloud &projected_points, bool copy_data_fields) + const std::vector &inliers, const Eigen::VectorXf &model_coefficients, PointCloud &projected_points, bool copy_data_fields) const { // Needs a valid model coefficients if (!isModelValid (model_coefficients)) @@ -335,7 +335,7 @@ pcl::SampleConsensusModelStick::projectPoints ( ////////////////////////////////////////////////////////////////////////// template bool pcl::SampleConsensusModelStick::doSamplesVerifyModel ( - const std::set &indices, const Eigen::VectorXf &model_coefficients, const double threshold) + const std::set &indices, const Eigen::VectorXf &model_coefficients, const double threshold) const { // Needs a valid set of model coefficients if (!isModelValid (model_coefficients)) diff --git a/sample_consensus/include/pcl/sample_consensus/model_types.h b/sample_consensus/include/pcl/sample_consensus/model_types.h index f07e88f9..4584aa98 100644 --- a/sample_consensus/include/pcl/sample_consensus/model_types.h +++ b/sample_consensus/include/pcl/sample_consensus/model_types.h @@ -41,8 +41,6 @@ #ifndef PCL_SAMPLE_CONSENSUS_MODEL_TYPES_H_ #define PCL_SAMPLE_CONSENSUS_MODEL_TYPES_H_ -#include - namespace pcl { enum SacModel @@ -68,35 +66,4 @@ namespace pcl }; } -typedef std::map::value_type SampleSizeModel; -// Warning: sample_size_pairs is deprecated and is kept only to prevent breaking existing user code. -// Starting from PCL 1.8.0 model sample size is a protected member of the SampleConsensusModel class. -const static SampleSizeModel sample_size_pairs[] = {SampleSizeModel (pcl::SACMODEL_PLANE, 3), - SampleSizeModel (pcl::SACMODEL_LINE, 2), - SampleSizeModel (pcl::SACMODEL_CIRCLE2D, 3), - SampleSizeModel (pcl::SACMODEL_CIRCLE3D, 3), - SampleSizeModel (pcl::SACMODEL_SPHERE, 4), - SampleSizeModel (pcl::SACMODEL_CYLINDER, 2), - SampleSizeModel (pcl::SACMODEL_CONE, 3), - //SampleSizeModel (pcl::SACMODEL_TORUS, 2), - SampleSizeModel (pcl::SACMODEL_PARALLEL_LINE, 2), - SampleSizeModel (pcl::SACMODEL_PERPENDICULAR_PLANE, 3), - //SampleSizeModel (pcl::PARALLEL_LINES, 2), - SampleSizeModel (pcl::SACMODEL_NORMAL_PLANE, 3), - SampleSizeModel (pcl::SACMODEL_NORMAL_SPHERE, 4), - SampleSizeModel (pcl::SACMODEL_REGISTRATION, 3), - SampleSizeModel (pcl::SACMODEL_REGISTRATION_2D, 3), - SampleSizeModel (pcl::SACMODEL_PARALLEL_PLANE, 3), - SampleSizeModel (pcl::SACMODEL_NORMAL_PARALLEL_PLANE, 3), - SampleSizeModel (pcl::SACMODEL_STICK, 2)}; - -namespace pcl -{ - const static std::map - PCL_DEPRECATED("This map is deprecated and is kept only to prevent breaking " - "existing user code. Starting from PCL 1.8.0 model sample size " - "is a protected member of the SampleConsensusModel class") - SAC_SAMPLE_SIZE (sample_size_pairs, sample_size_pairs + sizeof (sample_size_pairs) / sizeof (SampleSizeModel)); -} - #endif //#ifndef PCL_SAMPLE_CONSENSUS_MODEL_TYPES_H_ diff --git a/sample_consensus/include/pcl/sample_consensus/sac.h b/sample_consensus/include/pcl/sample_consensus/sac.h index 28cbbfd4..1b4d50a3 100644 --- a/sample_consensus/include/pcl/sample_consensus/sac.h +++ b/sample_consensus/include/pcl/sample_consensus/sac.h @@ -90,7 +90,7 @@ namespace pcl /** \brief Constructor for base SAC. * \param[in] model a Sample Consensus model - * \param[in] threshold distance to model threshol + * \param[in] threshold distance to model threshold * \param[in] random if true set the random seed to the current time, else set to 12345 (default: false) */ SampleConsensus (const SampleConsensusModelPtr &model, diff --git a/sample_consensus/include/pcl/sample_consensus/sac_model.h b/sample_consensus/include/pcl/sample_consensus/sac_model.h index f1b80556..1af4891a 100644 --- a/sample_consensus/include/pcl/sample_consensus/sac_model.h +++ b/sample_consensus/include/pcl/sample_consensus/sac_model.h @@ -216,9 +216,9 @@ namespace pcl * for creating a valid model * \param[out] model_coefficients the computed model coefficients */ - virtual bool - computeModelCoefficients (const std::vector &samples, - Eigen::VectorXf &model_coefficients) = 0; + virtual bool + computeModelCoefficients (const std::vector &samples, + Eigen::VectorXf &model_coefficients) const = 0; /** \brief Recompute the model coefficients using the given inlier set * and return them to the user. Pure virtual. @@ -230,19 +230,19 @@ namespace pcl * \param[in] model_coefficients the initial guess for the model coefficients * \param[out] optimized_coefficients the resultant recomputed coefficients after non-linear optimization */ - virtual void - optimizeModelCoefficients (const std::vector &inliers, + virtual void + optimizeModelCoefficients (const std::vector &inliers, const Eigen::VectorXf &model_coefficients, - Eigen::VectorXf &optimized_coefficients) = 0; + Eigen::VectorXf &optimized_coefficients) const = 0; /** \brief Compute all distances from the cloud data to a given model. Pure virtual. * * \param[in] model_coefficients the coefficients of a model that we need to compute distances to * \param[out] distances the resultant estimated distances */ - virtual void - getDistancesToModel (const Eigen::VectorXf &model_coefficients, - std::vector &distances) = 0; + virtual void + getDistancesToModel (const Eigen::VectorXf &model_coefficients, + std::vector &distances) const = 0; /** \brief Select all the points which respect the given model * coefficients as inliers. Pure virtual. @@ -267,8 +267,8 @@ namespace pcl * \return the resultant number of inliers */ virtual int - countWithinDistance (const Eigen::VectorXf &model_coefficients, - const double threshold) = 0; + countWithinDistance (const Eigen::VectorXf &model_coefficients, + const double threshold) const = 0; /** \brief Create a new point cloud with inliers projected onto the model. Pure virtual. * \param[in] inliers the data inliers that we want to project on the model @@ -278,11 +278,11 @@ namespace pcl * projected_points cloud to be an exact copy of the input dataset minus * the point projections on the plane model */ - virtual void - projectPoints (const std::vector &inliers, + virtual void + projectPoints (const std::vector &inliers, const Eigen::VectorXf &model_coefficients, - PointCloud &projected_points, - bool copy_data_fields = true) = 0; + PointCloud &projected_points, + bool copy_data_fields = true) const = 0; /** \brief Verify whether a subset of indices verifies a given set of * model coefficients. Pure virtual. @@ -293,9 +293,9 @@ namespace pcl * determining the inliers from the outliers */ virtual bool - doSamplesVerifyModel (const std::set &indices, - const Eigen::VectorXf &model_coefficients, - const double threshold) = 0; + doSamplesVerifyModel (const std::set &indices, + const Eigen::VectorXf &model_coefficients, + const double threshold) const = 0; /** \brief Provide a pointer to the input dataset * \param[in] cloud the const boost shared pointer to a PointCloud message @@ -389,12 +389,12 @@ namespace pcl * \param[out] max_radius the resultant maximum radius model */ inline void - getRadiusLimits (double &min_radius, double &max_radius) + getRadiusLimits (double &min_radius, double &max_radius) const { min_radius = radius_min_; max_radius = radius_max_; } - + /** \brief Set the maximum distance allowed when drawing random samples * \param[in] radius the maximum distance (L2 norm) * \param search @@ -411,7 +411,7 @@ namespace pcl * \param[out] radius the maximum distance (L2 norm) */ inline void - getSamplesMaxDist (double &radius) + getSamplesMaxDist (double &radius) const { radius = samples_radius_; } @@ -419,10 +419,10 @@ namespace pcl friend class ProgressiveSampleConsensus; /** \brief Compute the variance of the errors to the model. - * \param[in] error_sqr_dists a vector holding the distances - */ + * \param[in] error_sqr_dists a vector holding the distances + */ inline double - computeVariance (const std::vector &error_sqr_dists) + computeVariance (const std::vector &error_sqr_dists) const { std::vector dists (error_sqr_dists); const size_t medIdx = dists.size () >> 1; @@ -436,7 +436,7 @@ namespace pcl * selectWithinDistance must be called). */ inline double - computeVariance () + computeVariance () const { if (error_sqr_dists_.empty ()) { @@ -513,7 +513,7 @@ namespace pcl * \param[in] model_coefficients the set of model coefficients */ virtual bool - isModelValid (const Eigen::VectorXf &model_coefficients) + isModelValid (const Eigen::VectorXf &model_coefficients) const { if (model_coefficients.size () != model_size_) { @@ -647,7 +647,7 @@ namespace pcl /** Base functor all the models that need non linear optimization must * define their own one and implement operator() (const Eigen::VectorXd& x, Eigen::VectorXd& fvec) - * or operator() (const Eigen::VectorXf& x, Eigen::VectorXf& fvec) dependening on the choosen _Scalar + * or operator() (const Eigen::VectorXf& x, Eigen::VectorXf& fvec) depending on the chosen _Scalar */ template struct Functor diff --git a/sample_consensus/include/pcl/sample_consensus/sac_model_circle.h b/sample_consensus/include/pcl/sample_consensus/sac_model_circle.h index bcaeb551..07762ea2 100644 --- a/sample_consensus/include/pcl/sample_consensus/sac_model_circle.h +++ b/sample_consensus/include/pcl/sample_consensus/sac_model_circle.h @@ -78,7 +78,7 @@ namespace pcl * \param[in] random if true set the random seed to the current time, else set to 12345 (default: false) */ SampleConsensusModelCircle2D (const PointCloudConstPtr &cloud, bool random = false) - : SampleConsensusModel (cloud, random), tmp_inliers_ () + : SampleConsensusModel (cloud, random) { model_name_ = "SampleConsensusModelCircle2D"; sample_size_ = 3; @@ -93,7 +93,7 @@ namespace pcl SampleConsensusModelCircle2D (const PointCloudConstPtr &cloud, const std::vector &indices, bool random = false) - : SampleConsensusModel (cloud, indices, random), tmp_inliers_ () + : SampleConsensusModel (cloud, indices, random) { model_name_ = "SampleConsensusModelCircle2D"; sample_size_ = 3; @@ -104,7 +104,7 @@ namespace pcl * \param[in] source the model to copy into this */ SampleConsensusModelCircle2D (const SampleConsensusModelCircle2D &source) : - SampleConsensusModel (), tmp_inliers_ () + SampleConsensusModel () { *this = source; model_name_ = "SampleConsensusModelCircle2D"; @@ -120,7 +120,6 @@ namespace pcl operator = (const SampleConsensusModelCircle2D &source) { SampleConsensusModel::operator=(source); - tmp_inliers_ = source.tmp_inliers_; return (*this); } @@ -129,17 +128,17 @@ namespace pcl * \param[in] samples the point indices found as possible good candidates for creating a valid model * \param[out] model_coefficients the resultant model coefficients */ - bool - computeModelCoefficients (const std::vector &samples, - Eigen::VectorXf &model_coefficients); + bool + computeModelCoefficients (const std::vector &samples, + Eigen::VectorXf &model_coefficients) const; /** \brief Compute all distances from the cloud data to a given 2D circle model. * \param[in] model_coefficients the coefficients of a 2D circle model that we need to compute distances to * \param[out] distances the resultant estimated distances */ - void - getDistancesToModel (const Eigen::VectorXf &model_coefficients, - std::vector &distances); + void + getDistancesToModel (const Eigen::VectorXf &model_coefficients, + std::vector &distances) const; /** \brief Compute all distances from the cloud data to a given 2D circle model. * \param[in] model_coefficients the coefficients of a 2D circle model that we need to compute distances to @@ -158,8 +157,8 @@ namespace pcl * \return the resultant number of inliers */ virtual int - countWithinDistance (const Eigen::VectorXf &model_coefficients, - const double threshold); + countWithinDistance (const Eigen::VectorXf &model_coefficients, + const double threshold) const; /** \brief Recompute the 2d circle coefficients using the given inlier set and return them to the user. * @note: these are the coefficients of the 2d circle model after refinement (e.g. after SVD) @@ -167,10 +166,10 @@ namespace pcl * \param[in] model_coefficients the initial guess for the optimization * \param[out] optimized_coefficients the resultant recomputed coefficients after non-linear optimization */ - void - optimizeModelCoefficients (const std::vector &inliers, - const Eigen::VectorXf &model_coefficients, - Eigen::VectorXf &optimized_coefficients); + void + optimizeModelCoefficients (const std::vector &inliers, + const Eigen::VectorXf &model_coefficients, + Eigen::VectorXf &optimized_coefficients) const; /** \brief Create a new point cloud with inliers projected onto the 2d circle model. * \param[in] inliers the data inliers that we want to project on the 2d circle model @@ -178,21 +177,21 @@ namespace pcl * \param[out] projected_points the resultant projected points * \param[in] copy_data_fields set to true if we need to copy the other data fields */ - void - projectPoints (const std::vector &inliers, - const Eigen::VectorXf &model_coefficients, - PointCloud &projected_points, - bool copy_data_fields = true); + void + projectPoints (const std::vector &inliers, + const Eigen::VectorXf &model_coefficients, + PointCloud &projected_points, + bool copy_data_fields = true) const; /** \brief Verify whether a subset of indices verifies the given 2d circle model coefficients. * \param[in] indices the data indices that need to be tested against the 2d circle model * \param[in] model_coefficients the 2d circle model coefficients * \param[in] threshold a maximum admissible distance threshold for determining the inliers from the outliers */ - bool - doSamplesVerifyModel (const std::set &indices, - const Eigen::VectorXf &model_coefficients, - const double threshold); + bool + doSamplesVerifyModel (const std::set &indices, + const Eigen::VectorXf &model_coefficients, + const double threshold) const; /** \brief Return an unique id for this model (SACMODEL_CIRCLE2D). */ inline pcl::SacModel @@ -206,7 +205,7 @@ namespace pcl * \param[in] model_coefficients the set of model coefficients */ virtual bool - isModelValid (const Eigen::VectorXf &model_coefficients); + isModelValid (const Eigen::VectorXf &model_coefficients) const; /** \brief Check if a sample of indices results in a good sample of points indices. * \param[in] samples the resultant index samples @@ -215,8 +214,6 @@ namespace pcl isSampleGood(const std::vector &samples) const; private: - /** \brief Temporary pointer to a list of given indices for optimizeModelCoefficients () */ - const std::vector *tmp_inliers_; #if defined BUILD_Maintainer && defined __GNUC__ && __GNUC__ == 4 && __GNUC_MINOR__ > 3 #pragma GCC diagnostic ignored "-Weffc++" @@ -225,12 +222,11 @@ namespace pcl struct OptimizationFunctor : pcl::Functor { /** \brief Functor constructor - * \param[in] m_data_points the number of data points to evaluate + * \param[in] indices the indices of data points to evaluate * \param[in] estimator pointer to the estimator object - * \param[in] distance distance computation function pointer */ - OptimizationFunctor (int m_data_points, pcl::SampleConsensusModelCircle2D *model) : - pcl::Functor(m_data_points), model_ (model) {} + OptimizationFunctor (const pcl::SampleConsensusModelCircle2D *model, const std::vector& indices) : + pcl::Functor (indices.size ()), model_ (model), indices_ (indices) {} /** Cost function to be minimized * \param[in] x the variables array @@ -243,16 +239,17 @@ namespace pcl for (int i = 0; i < values (); ++i) { // Compute the difference between the center of the circle and the datapoint X_i - float xt = model_->input_->points[(*model_->tmp_inliers_)[i]].x - x[0]; - float yt = model_->input_->points[(*model_->tmp_inliers_)[i]].y - x[1]; - + float xt = model_->input_->points[indices_[i]].x - x[0]; + float yt = model_->input_->points[indices_[i]].y - x[1]; + // g = sqrt ((x-a)^2 + (y-b)^2) - R fvec[i] = std::sqrt (xt * xt + yt * yt) - x[2]; } return (0); } - pcl::SampleConsensusModelCircle2D *model_; + const pcl::SampleConsensusModelCircle2D *model_; + const std::vector &indices_; }; #if defined BUILD_Maintainer && defined __GNUC__ && __GNUC__ == 4 && __GNUC_MINOR__ > 3 #pragma GCC diagnostic warning "-Weffc++" diff --git a/sample_consensus/include/pcl/sample_consensus/sac_model_circle3d.h b/sample_consensus/include/pcl/sample_consensus/sac_model_circle3d.h index 54dddd5f..af50086f 100644 --- a/sample_consensus/include/pcl/sample_consensus/sac_model_circle3d.h +++ b/sample_consensus/include/pcl/sample_consensus/sac_model_circle3d.h @@ -109,7 +109,7 @@ namespace pcl * \param[in] source the model to copy into this */ SampleConsensusModelCircle3D (const SampleConsensusModelCircle3D &source) : - SampleConsensusModel (), tmp_inliers_ () + SampleConsensusModel () { *this = source; model_name_ = "SampleConsensusModelCircle3D"; @@ -122,7 +122,6 @@ namespace pcl operator = (const SampleConsensusModelCircle3D &source) { SampleConsensusModel::operator=(source); - tmp_inliers_ = source.tmp_inliers_; return (*this); } @@ -133,7 +132,7 @@ namespace pcl */ bool computeModelCoefficients (const std::vector &samples, - Eigen::VectorXf &model_coefficients); + Eigen::VectorXf &model_coefficients) const; /** \brief Compute all distances from the cloud data to a given 3D circle model. * \param[in] model_coefficients the coefficients of a 2D circle model that we need to compute distances to @@ -141,7 +140,7 @@ namespace pcl */ void getDistancesToModel (const Eigen::VectorXf &model_coefficients, - std::vector &distances); + std::vector &distances) const; /** \brief Compute all distances from the cloud data to a given 3D circle model. * \param[in] model_coefficients the coefficients of a 3D circle model that we need to compute distances to @@ -161,7 +160,7 @@ namespace pcl */ virtual int countWithinDistance (const Eigen::VectorXf &model_coefficients, - const double threshold); + const double threshold) const; /** \brief Recompute the 3d circle coefficients using the given inlier set and return them to the user. * @note: these are the coefficients of the 3d circle model after refinement (e.g. after SVD) @@ -172,7 +171,7 @@ namespace pcl void optimizeModelCoefficients (const std::vector &inliers, const Eigen::VectorXf &model_coefficients, - Eigen::VectorXf &optimized_coefficients); + Eigen::VectorXf &optimized_coefficients) const; /** \brief Create a new point cloud with inliers projected onto the 3d circle model. * \param[in] inliers the data inliers that we want to project on the 3d circle model @@ -184,7 +183,7 @@ namespace pcl projectPoints (const std::vector &inliers, const Eigen::VectorXf &model_coefficients, PointCloud &projected_points, - bool copy_data_fields = true); + bool copy_data_fields = true) const; /** \brief Verify whether a subset of indices verifies the given 3d circle model coefficients. * \param[in] indices the data indices that need to be tested against the 3d circle model @@ -194,7 +193,7 @@ namespace pcl bool doSamplesVerifyModel (const std::set &indices, const Eigen::VectorXf &model_coefficients, - const double threshold); + const double threshold) const; /** \brief Return an unique id for this model (SACMODEL_CIRCLE3D). */ inline pcl::SacModel @@ -208,7 +207,7 @@ namespace pcl * \param[in] model_coefficients the set of model coefficients */ virtual bool - isModelValid (const Eigen::VectorXf &model_coefficients); + isModelValid (const Eigen::VectorXf &model_coefficients) const; /** \brief Check if a sample of indices results in a good sample of points indices. * \param[in] samples the resultant index samples @@ -217,19 +216,15 @@ namespace pcl isSampleGood(const std::vector &samples) const; private: - /** \brief Temporary pointer to a list of given indices for optimizeModelCoefficients () */ - const std::vector *tmp_inliers_; - /** \brief Functor for the optimization function */ struct OptimizationFunctor : pcl::Functor { /** Functor constructor - * \param[in] m_data_points the number of functions - * \param[in] estimator pointer to the estimator object - * \param[in] distance distance computation function pointer - */ - OptimizationFunctor (int m_data_points, pcl::SampleConsensusModelCircle3D *model) : - pcl::Functor (m_data_points), model_ (model) {} + * \param[in] indices the indices of data points to evaluate + * \param[in] estimator pointer to the estimator object + */ + OptimizationFunctor (const pcl::SampleConsensusModelCircle3D *model, const std::vector& indices) : + pcl::Functor (indices.size ()), model_ (model), indices_ (indices) {} /** Cost function to be minimized * \param[in] x the variables array @@ -242,7 +237,7 @@ namespace pcl { // what i have: // P : Sample Point - Eigen::Vector3d P (model_->input_->points[(*model_->tmp_inliers_)[i]].x, model_->input_->points[(*model_->tmp_inliers_)[i]].y, model_->input_->points[(*model_->tmp_inliers_)[i]].z); + Eigen::Vector3d P (model_->input_->points[indices_[i]].x, model_->input_->points[indices_[i]].y, model_->input_->points[indices_[i]].z); // C : Circle Center Eigen::Vector3d C (x[0], x[1], x[2]); // N : Circle (Plane) Normal @@ -267,7 +262,8 @@ namespace pcl return (0); } - pcl::SampleConsensusModelCircle3D *model_; + const pcl::SampleConsensusModelCircle3D *model_; + const std::vector &indices_; }; }; } diff --git a/sample_consensus/include/pcl/sample_consensus/sac_model_cone.h b/sample_consensus/include/pcl/sample_consensus/sac_model_cone.h index e4593033..18708306 100644 --- a/sample_consensus/include/pcl/sample_consensus/sac_model_cone.h +++ b/sample_consensus/include/pcl/sample_consensus/sac_model_cone.h @@ -41,7 +41,6 @@ #include #include -#include #include #include #include @@ -92,7 +91,6 @@ namespace pcl , eps_angle_ (0) , min_angle_ (-std::numeric_limits::max ()) , max_angle_ (std::numeric_limits::max ()) - , tmp_inliers_ () { model_name_ = "SampleConsensusModelCone"; sample_size_ = 3; @@ -113,7 +111,6 @@ namespace pcl , eps_angle_ (0) , min_angle_ (-std::numeric_limits::max ()) , max_angle_ (std::numeric_limits::max ()) - , tmp_inliers_ () { model_name_ = "SampleConsensusModelCone"; sample_size_ = 3; @@ -126,7 +123,7 @@ namespace pcl SampleConsensusModelCone (const SampleConsensusModelCone &source) : SampleConsensusModel (), SampleConsensusModelFromNormals (), - axis_ (), eps_angle_ (), min_angle_ (), max_angle_ (), tmp_inliers_ () + axis_ (), eps_angle_ (), min_angle_ (), max_angle_ () { *this = source; model_name_ = "SampleConsensusModelCone"; @@ -147,7 +144,6 @@ namespace pcl eps_angle_ = source.eps_angle_; min_angle_ = source.min_angle_; max_angle_ = source.max_angle_; - tmp_inliers_ = source.tmp_inliers_; return (*this); } @@ -200,17 +196,17 @@ namespace pcl * \param[in] samples the point indices found as possible good candidates for creating a valid model * \param[out] model_coefficients the resultant model coefficients */ - bool - computeModelCoefficients (const std::vector &samples, - Eigen::VectorXf &model_coefficients); + bool + computeModelCoefficients (const std::vector &samples, + Eigen::VectorXf &model_coefficients) const; /** \brief Compute all distances from the cloud data to a given cone model. * \param[in] model_coefficients the coefficients of a cone model that we need to compute distances to * \param[out] distances the resultant estimated distances */ - void - getDistancesToModel (const Eigen::VectorXf &model_coefficients, - std::vector &distances); + void + getDistancesToModel (const Eigen::VectorXf &model_coefficients, + std::vector &distances) const; /** \brief Select all the points which respect the given model coefficients as inliers. * \param[in] model_coefficients the coefficients of a cone model that we need to compute distances to @@ -229,8 +225,8 @@ namespace pcl * \return the resultant number of inliers */ virtual int - countWithinDistance (const Eigen::VectorXf &model_coefficients, - const double threshold); + countWithinDistance (const Eigen::VectorXf &model_coefficients, + const double threshold) const; /** \brief Recompute the cone coefficients using the given inlier set and return them to the user. @@ -239,10 +235,10 @@ namespace pcl * \param[in] model_coefficients the initial guess for the optimization * \param[out] optimized_coefficients the resultant recomputed coefficients after non-linear optimization */ - void - optimizeModelCoefficients (const std::vector &inliers, - const Eigen::VectorXf &model_coefficients, - Eigen::VectorXf &optimized_coefficients); + void + optimizeModelCoefficients (const std::vector &inliers, + const Eigen::VectorXf &model_coefficients, + Eigen::VectorXf &optimized_coefficients) const; /** \brief Create a new point cloud with inliers projected onto the cone model. @@ -251,21 +247,21 @@ namespace pcl * \param[out] projected_points the resultant projected points * \param[in] copy_data_fields set to true if we need to copy the other data fields */ - void - projectPoints (const std::vector &inliers, - const Eigen::VectorXf &model_coefficients, - PointCloud &projected_points, - bool copy_data_fields = true); + void + projectPoints (const std::vector &inliers, + const Eigen::VectorXf &model_coefficients, + PointCloud &projected_points, + bool copy_data_fields = true) const; /** \brief Verify whether a subset of indices verifies the given cone model coefficients. * \param[in] indices the data indices that need to be tested against the cone model * \param[in] model_coefficients the cone model coefficients * \param[in] threshold a maximum admissible distance threshold for determining the inliers from the outliers */ - bool - doSamplesVerifyModel (const std::set &indices, - const Eigen::VectorXf &model_coefficients, - const double threshold); + bool + doSamplesVerifyModel (const std::set &indices, + const Eigen::VectorXf &model_coefficients, + const double threshold) const; /** \brief Return an unique id for this model (SACMODEL_CONE). */ inline pcl::SacModel @@ -279,20 +275,14 @@ namespace pcl * \param[in] pt a point * \param[in] model_coefficients the line coefficients (a point on the line, line direction) */ - double - pointToAxisDistance (const Eigen::Vector4f &pt, const Eigen::VectorXf &model_coefficients); - - /** \brief Get a string representation of the name of this class. */ - PCL_DEPRECATED ("[pcl::SampleConsensusModelCone::getName] getName is deprecated. Please use getClassName instead.") - std::string - getName () const { return (model_name_); } + double + pointToAxisDistance (const Eigen::Vector4f &pt, const Eigen::VectorXf &model_coefficients) const; - protected: /** \brief Check whether a model is valid given the user constraints. * \param[in] model_coefficients the set of model coefficients */ virtual bool - isModelValid (const Eigen::VectorXf &model_coefficients); + isModelValid (const Eigen::VectorXf &model_coefficients) const; /** \brief Check if a sample of indices results in a good sample of points * indices. Pure virtual. @@ -312,9 +302,6 @@ namespace pcl double min_angle_; double max_angle_; - /** \brief temporary pointer to a list of given indices for optimizeModelCoefficients () */ - const std::vector *tmp_inliers_; - #if defined BUILD_Maintainer && defined __GNUC__ && __GNUC__ == 4 && __GNUC_MINOR__ > 3 #pragma GCC diagnostic ignored "-Weffc++" #endif @@ -322,12 +309,11 @@ namespace pcl struct OptimizationFunctor : pcl::Functor { /** Functor constructor - * \param[in] m_data_points the number of data points to evaluate + * \param[in] indices the indices of data points to evaluate * \param[in] estimator pointer to the estimator object - * \param[in] distance distance computation function pointer */ - OptimizationFunctor (int m_data_points, pcl::SampleConsensusModelCone *model) : - pcl::Functor (m_data_points), model_ (model) {} + OptimizationFunctor (const pcl::SampleConsensusModelCone *model, const std::vector& indices) : + pcl::Functor (indices.size ()), model_ (model), indices_ (indices) {} /** Cost function to be minimized * \param[in] x variables array @@ -347,9 +333,9 @@ namespace pcl for (int i = 0; i < values (); ++i) { // dist = f - r - Eigen::Vector4f pt (model_->input_->points[(*model_->tmp_inliers_)[i]].x, - model_->input_->points[(*model_->tmp_inliers_)[i]].y, - model_->input_->points[(*model_->tmp_inliers_)[i]].z, 0); + Eigen::Vector4f pt (model_->input_->points[indices_[i]].x, + model_->input_->points[indices_[i]].y, + model_->input_->points[indices_[i]].z, 0); // Calculate the point's projection on the cone axis float k = (pt.dot (axis_dir) - apexdotdir) * dirdotdir; @@ -364,7 +350,8 @@ namespace pcl return (0); } - pcl::SampleConsensusModelCone *model_; + const pcl::SampleConsensusModelCone *model_; + const std::vector &indices_; }; #if defined BUILD_Maintainer && defined __GNUC__ && __GNUC__ == 4 && __GNUC_MINOR__ > 3 #pragma GCC diagnostic warning "-Weffc++" diff --git a/sample_consensus/include/pcl/sample_consensus/sac_model_cylinder.h b/sample_consensus/include/pcl/sample_consensus/sac_model_cylinder.h index 2c6b080e..c347c00f 100644 --- a/sample_consensus/include/pcl/sample_consensus/sac_model_cylinder.h +++ b/sample_consensus/include/pcl/sample_consensus/sac_model_cylinder.h @@ -43,7 +43,6 @@ #include #include -#include #include #include @@ -90,7 +89,6 @@ namespace pcl , SampleConsensusModelFromNormals () , axis_ (Eigen::Vector3f::Zero ()) , eps_angle_ (0) - , tmp_inliers_ () { model_name_ = "SampleConsensusModelCylinder"; sample_size_ = 2; @@ -109,7 +107,6 @@ namespace pcl , SampleConsensusModelFromNormals () , axis_ (Eigen::Vector3f::Zero ()) , eps_angle_ (0) - , tmp_inliers_ () { model_name_ = "SampleConsensusModelCylinder"; sample_size_ = 2; @@ -123,8 +120,7 @@ namespace pcl SampleConsensusModel (), SampleConsensusModelFromNormals (), axis_ (Eigen::Vector3f::Zero ()), - eps_angle_ (0), - tmp_inliers_ () + eps_angle_ (0) { *this = source; model_name_ = "SampleConsensusModelCylinder"; @@ -143,7 +139,6 @@ namespace pcl SampleConsensusModelFromNormals::operator=(source); axis_ = source.axis_; eps_angle_ = source.eps_angle_; - tmp_inliers_ = source.tmp_inliers_; return (*this); } @@ -173,17 +168,17 @@ namespace pcl * \param[in] samples the point indices found as possible good candidates for creating a valid model * \param[out] model_coefficients the resultant model coefficients */ - bool - computeModelCoefficients (const std::vector &samples, - Eigen::VectorXf &model_coefficients); + bool + computeModelCoefficients (const std::vector &samples, + Eigen::VectorXf &model_coefficients) const; /** \brief Compute all distances from the cloud data to a given cylinder model. * \param[in] model_coefficients the coefficients of a cylinder model that we need to compute distances to * \param[out] distances the resultant estimated distances */ - void - getDistancesToModel (const Eigen::VectorXf &model_coefficients, - std::vector &distances); + void + getDistancesToModel (const Eigen::VectorXf &model_coefficients, + std::vector &distances) const; /** \brief Select all the points which respect the given model coefficients as inliers. * \param[in] model_coefficients the coefficients of a cylinder model that we need to compute distances to @@ -202,8 +197,8 @@ namespace pcl * \return the resultant number of inliers */ virtual int - countWithinDistance (const Eigen::VectorXf &model_coefficients, - const double threshold); + countWithinDistance (const Eigen::VectorXf &model_coefficients, + const double threshold) const; /** \brief Recompute the cylinder coefficients using the given inlier set and return them to the user. * @note: these are the coefficients of the cylinder model after refinement (e.g. after SVD) @@ -211,10 +206,10 @@ namespace pcl * \param[in] model_coefficients the initial guess for the optimization * \param[out] optimized_coefficients the resultant recomputed coefficients after non-linear optimization */ - void - optimizeModelCoefficients (const std::vector &inliers, - const Eigen::VectorXf &model_coefficients, - Eigen::VectorXf &optimized_coefficients); + void + optimizeModelCoefficients (const std::vector &inliers, + const Eigen::VectorXf &model_coefficients, + Eigen::VectorXf &optimized_coefficients) const; /** \brief Create a new point cloud with inliers projected onto the cylinder model. @@ -223,21 +218,21 @@ namespace pcl * \param[out] projected_points the resultant projected points * \param[in] copy_data_fields set to true if we need to copy the other data fields */ - void - projectPoints (const std::vector &inliers, - const Eigen::VectorXf &model_coefficients, - PointCloud &projected_points, - bool copy_data_fields = true); + void + projectPoints (const std::vector &inliers, + const Eigen::VectorXf &model_coefficients, + PointCloud &projected_points, + bool copy_data_fields = true) const; /** \brief Verify whether a subset of indices verifies the given cylinder model coefficients. * \param[in] indices the data indices that need to be tested against the cylinder model * \param[in] model_coefficients the cylinder model coefficients * \param[in] threshold a maximum admissible distance threshold for determining the inliers from the outliers */ - bool - doSamplesVerifyModel (const std::set &indices, - const Eigen::VectorXf &model_coefficients, - const double threshold); + bool + doSamplesVerifyModel (const std::set &indices, + const Eigen::VectorXf &model_coefficients, + const double threshold) const; /** \brief Return an unique id for this model (SACMODEL_CYLINDER). */ inline pcl::SacModel @@ -251,8 +246,8 @@ namespace pcl * \param[in] pt a point * \param[in] model_coefficients the line coefficients (a point on the line, line direction) */ - double - pointToLineDistance (const Eigen::Vector4f &pt, const Eigen::VectorXf &model_coefficients); + double + pointToLineDistance (const Eigen::Vector4f &pt, const Eigen::VectorXf &model_coefficients) const; /** \brief Project a point onto a line given by a point and a direction vector * \param[in] pt the input point to project @@ -261,10 +256,10 @@ namespace pcl * \param[out] pt_proj the resultant projected point */ inline void - projectPointToLine (const Eigen::Vector4f &pt, - const Eigen::Vector4f &line_pt, + projectPointToLine (const Eigen::Vector4f &pt, + const Eigen::Vector4f &line_pt, const Eigen::Vector4f &line_dir, - Eigen::Vector4f &pt_proj) + Eigen::Vector4f &pt_proj) const { float k = (pt.dot (line_dir) - line_pt.dot (line_dir)) / line_dir.dot (line_dir); // Calculate the projection of the point on the line @@ -277,22 +272,16 @@ namespace pcl * \param[in] model_coefficients the coefficients of the cylinder (point_on_axis, axis_direction, cylinder_radius_R) * \param[out] pt_proj the resultant projected point */ - void - projectPointToCylinder (const Eigen::Vector4f &pt, - const Eigen::VectorXf &model_coefficients, - Eigen::Vector4f &pt_proj); - - /** \brief Get a string representation of the name of this class. */ - PCL_DEPRECATED ("[pcl::SampleConsensusModelCylinder::getName] getName is deprecated. Please use getClassName instead.") - std::string - getName () const { return (model_name_); } + void + projectPointToCylinder (const Eigen::Vector4f &pt, + const Eigen::VectorXf &model_coefficients, + Eigen::Vector4f &pt_proj) const; - protected: /** \brief Check whether a model is valid given the user constraints. * \param[in] model_coefficients the set of model coefficients */ virtual bool - isModelValid (const Eigen::VectorXf &model_coefficients); + isModelValid (const Eigen::VectorXf &model_coefficients) const; /** \brief Check if a sample of indices results in a good sample of points * indices. Pure virtual. @@ -308,9 +297,6 @@ namespace pcl /** \brief The maximum allowed difference between the plane normal and the given axis. */ double eps_angle_; - /** \brief temporary pointer to a list of given indices for optimizeModelCoefficients () */ - const std::vector *tmp_inliers_; - #if defined BUILD_Maintainer && defined __GNUC__ && __GNUC__ == 4 && __GNUC_MINOR__ > 3 #pragma GCC diagnostic ignored "-Weffc++" #endif @@ -318,12 +304,11 @@ namespace pcl struct OptimizationFunctor : pcl::Functor { /** Functor constructor - * \param[in] m_data_points the number of data points to evaluate + * \param[in] indices the indices of data points to evaluate * \param[in] estimator pointer to the estimator object - * \param[in] distance distance computation function pointer */ - OptimizationFunctor (int m_data_points, pcl::SampleConsensusModelCylinder *model) : - pcl::Functor (m_data_points), model_ (model) {} + OptimizationFunctor (const pcl::SampleConsensusModelCylinder *model, const std::vector& indices) : + pcl::Functor (indices.size ()), model_ (model), indices_ (indices) {} /** Cost function to be minimized * \param[in] x variables array @@ -339,16 +324,17 @@ namespace pcl for (int i = 0; i < values (); ++i) { // dist = f - r - Eigen::Vector4f pt (model_->input_->points[(*model_->tmp_inliers_)[i]].x, - model_->input_->points[(*model_->tmp_inliers_)[i]].y, - model_->input_->points[(*model_->tmp_inliers_)[i]].z, 0); + Eigen::Vector4f pt (model_->input_->points[indices_[i]].x, + model_->input_->points[indices_[i]].y, + model_->input_->points[indices_[i]].z, 0); fvec[i] = static_cast (pcl::sqrPointToLineDistance (pt, line_pt, line_dir) - x[6]*x[6]); } return (0); } - pcl::SampleConsensusModelCylinder *model_; + const pcl::SampleConsensusModelCylinder *model_; + const std::vector &indices_; }; #if defined BUILD_Maintainer && defined __GNUC__ && __GNUC__ == 4 && __GNUC_MINOR__ > 3 #pragma GCC diagnostic warning "-Weffc++" diff --git a/sample_consensus/include/pcl/sample_consensus/sac_model_line.h b/sample_consensus/include/pcl/sample_consensus/sac_model_line.h index 07eef590..7b383465 100644 --- a/sample_consensus/include/pcl/sample_consensus/sac_model_line.h +++ b/sample_consensus/include/pcl/sample_consensus/sac_model_line.h @@ -111,17 +111,17 @@ namespace pcl * \param[in] samples the point indices found as possible good candidates for creating a valid model * \param[out] model_coefficients the resultant model coefficients */ - bool - computeModelCoefficients (const std::vector &samples, - Eigen::VectorXf &model_coefficients); + bool + computeModelCoefficients (const std::vector &samples, + Eigen::VectorXf &model_coefficients) const; /** \brief Compute all squared distances from the cloud data to a given line model. * \param[in] model_coefficients the coefficients of a line model that we need to compute distances to * \param[out] distances the resultant estimated squared distances */ - void - getDistancesToModel (const Eigen::VectorXf &model_coefficients, - std::vector &distances); + void + getDistancesToModel (const Eigen::VectorXf &model_coefficients, + std::vector &distances) const; /** \brief Select all the points which respect the given model coefficients as inliers. * \param[in] model_coefficients the coefficients of a line model that we need to compute distances to @@ -140,8 +140,8 @@ namespace pcl * \return the resultant number of inliers */ virtual int - countWithinDistance (const Eigen::VectorXf &model_coefficients, - const double threshold); + countWithinDistance (const Eigen::VectorXf &model_coefficients, + const double threshold) const; /** \brief Recompute the line coefficients using the given inlier set and return them to the user. * @note: these are the coefficients of the line model after refinement (e.g. after SVD) @@ -149,10 +149,10 @@ namespace pcl * \param[in] model_coefficients the initial guess for the model coefficients * \param[out] optimized_coefficients the resultant recomputed coefficients after optimization */ - void - optimizeModelCoefficients (const std::vector &inliers, - const Eigen::VectorXf &model_coefficients, - Eigen::VectorXf &optimized_coefficients); + void + optimizeModelCoefficients (const std::vector &inliers, + const Eigen::VectorXf &model_coefficients, + Eigen::VectorXf &optimized_coefficients) const; /** \brief Create a new point cloud with inliers projected onto the line model. * \param[in] inliers the data inliers that we want to project on the line model @@ -160,21 +160,21 @@ namespace pcl * \param[out] projected_points the resultant projected points * \param[in] copy_data_fields set to true if we need to copy the other data fields */ - void - projectPoints (const std::vector &inliers, - const Eigen::VectorXf &model_coefficients, - PointCloud &projected_points, - bool copy_data_fields = true); + void + projectPoints (const std::vector &inliers, + const Eigen::VectorXf &model_coefficients, + PointCloud &projected_points, + bool copy_data_fields = true) const; /** \brief Verify whether a subset of indices verifies the given line model coefficients. * \param[in] indices the data indices that need to be tested against the line model * \param[in] model_coefficients the line model coefficients * \param[in] threshold a maximum admissible distance threshold for determining the inliers from the outliers */ - bool - doSamplesVerifyModel (const std::set &indices, - const Eigen::VectorXf &model_coefficients, - const double threshold); + bool + doSamplesVerifyModel (const std::set &indices, + const Eigen::VectorXf &model_coefficients, + const double threshold) const; /** \brief Return an unique id for this model (SACMODEL_LINE). */ inline pcl::SacModel diff --git a/sample_consensus/include/pcl/sample_consensus/sac_model_normal_parallel_plane.h b/sample_consensus/include/pcl/sample_consensus/sac_model_normal_parallel_plane.h index 2b29b667..b828bbd5 100644 --- a/sample_consensus/include/pcl/sample_consensus/sac_model_normal_parallel_plane.h +++ b/sample_consensus/include/pcl/sample_consensus/sac_model_normal_parallel_plane.h @@ -196,7 +196,7 @@ namespace pcl * \param[in] model_coefficients the set of model coefficients */ virtual bool - isModelValid (const Eigen::VectorXf &model_coefficients); + isModelValid (const Eigen::VectorXf &model_coefficients) const; private: /** \brief The axis along which we need to search for a plane perpendicular to. */ diff --git a/sample_consensus/include/pcl/sample_consensus/sac_model_normal_plane.h b/sample_consensus/include/pcl/sample_consensus/sac_model_normal_plane.h index 1e762ba3..509f94a7 100644 --- a/sample_consensus/include/pcl/sample_consensus/sac_model_normal_plane.h +++ b/sample_consensus/include/pcl/sample_consensus/sac_model_normal_plane.h @@ -143,16 +143,16 @@ namespace pcl * \return the resultant number of inliers */ virtual int - countWithinDistance (const Eigen::VectorXf &model_coefficients, - const double threshold); + countWithinDistance (const Eigen::VectorXf &model_coefficients, + const double threshold) const; /** \brief Compute all distances from the cloud data to a given plane model. * \param[in] model_coefficients the coefficients of a plane model that we need to compute distances to * \param[out] distances the resultant estimated distances */ - void - getDistancesToModel (const Eigen::VectorXf &model_coefficients, - std::vector &distances); + void + getDistancesToModel (const Eigen::VectorXf &model_coefficients, + std::vector &distances) const; /** \brief Return an unique id for this model (SACMODEL_NORMAL_PLANE). */ inline pcl::SacModel diff --git a/sample_consensus/include/pcl/sample_consensus/sac_model_normal_sphere.h b/sample_consensus/include/pcl/sample_consensus/sac_model_normal_sphere.h index 9a40dbf6..ef5f27e5 100644 --- a/sample_consensus/include/pcl/sample_consensus/sac_model_normal_sphere.h +++ b/sample_consensus/include/pcl/sample_consensus/sac_model_normal_sphere.h @@ -136,16 +136,16 @@ namespace pcl * \return the resultant number of inliers */ virtual int - countWithinDistance (const Eigen::VectorXf &model_coefficients, - const double threshold); + countWithinDistance (const Eigen::VectorXf &model_coefficients, + const double threshold) const; /** \brief Compute all distances from the cloud data to a given sphere model. * \param[in] model_coefficients the coefficients of a sphere model that we need to compute distances to * \param[out] distances the resultant estimated distances */ - void - getDistancesToModel (const Eigen::VectorXf &model_coefficients, - std::vector &distances); + void + getDistancesToModel (const Eigen::VectorXf &model_coefficients, + std::vector &distances) const; /** \brief Return an unique id for this model (SACMODEL_NORMAL_SPHERE). */ inline pcl::SacModel @@ -161,7 +161,7 @@ namespace pcl * \param[in] model_coefficients the set of model coefficients */ virtual bool - isModelValid (const Eigen::VectorXf &model_coefficients); + isModelValid (const Eigen::VectorXf &model_coefficients) const; }; } diff --git a/sample_consensus/include/pcl/sample_consensus/sac_model_parallel_line.h b/sample_consensus/include/pcl/sample_consensus/sac_model_parallel_line.h index 72fda46b..afff15c1 100644 --- a/sample_consensus/include/pcl/sample_consensus/sac_model_parallel_line.h +++ b/sample_consensus/include/pcl/sample_consensus/sac_model_parallel_line.h @@ -146,7 +146,7 @@ namespace pcl */ virtual int countWithinDistance (const Eigen::VectorXf &model_coefficients, - const double threshold); + const double threshold) const; /** \brief Compute all squared distances from the cloud data to a given line model. * \param[in] model_coefficients the coefficients of a line model that we need to compute distances to @@ -154,7 +154,7 @@ namespace pcl */ void getDistancesToModel (const Eigen::VectorXf &model_coefficients, - std::vector &distances); + std::vector &distances) const; /** \brief Return an unique id for this model (SACMODEL_PARALLEL_LINE). */ inline pcl::SacModel @@ -168,7 +168,7 @@ namespace pcl * \param[in] model_coefficients the set of model coefficients */ virtual bool - isModelValid (const Eigen::VectorXf &model_coefficients); + isModelValid (const Eigen::VectorXf &model_coefficients) const; /** \brief The axis along which we need to search for a line. */ Eigen::Vector3f axis_; diff --git a/sample_consensus/include/pcl/sample_consensus/sac_model_parallel_plane.h b/sample_consensus/include/pcl/sample_consensus/sac_model_parallel_plane.h index 7e681b1b..4807be6c 100644 --- a/sample_consensus/include/pcl/sample_consensus/sac_model_parallel_plane.h +++ b/sample_consensus/include/pcl/sample_consensus/sac_model_parallel_plane.h @@ -150,7 +150,7 @@ namespace pcl */ virtual int countWithinDistance (const Eigen::VectorXf &model_coefficients, - const double threshold); + const double threshold) const; /** \brief Compute all distances from the cloud data to a given plane model. * \param[in] model_coefficients the coefficients of a plane model that we need to compute distances to @@ -158,7 +158,7 @@ namespace pcl */ void getDistancesToModel (const Eigen::VectorXf &model_coefficients, - std::vector &distances); + std::vector &distances) const; /** \brief Return an unique id for this model (SACMODEL_PARALLEL_PLANE). */ inline pcl::SacModel @@ -172,7 +172,7 @@ namespace pcl * \param[in] model_coefficients the set of model coefficients */ virtual bool - isModelValid (const Eigen::VectorXf &model_coefficients); + isModelValid (const Eigen::VectorXf &model_coefficients) const; /** \brief The axis along which we need to search for a plane perpendicular to. */ Eigen::Vector3f axis_; diff --git a/sample_consensus/include/pcl/sample_consensus/sac_model_perpendicular_plane.h b/sample_consensus/include/pcl/sample_consensus/sac_model_perpendicular_plane.h index 43e7d0e1..91ff7bcf 100644 --- a/sample_consensus/include/pcl/sample_consensus/sac_model_perpendicular_plane.h +++ b/sample_consensus/include/pcl/sample_consensus/sac_model_perpendicular_plane.h @@ -152,16 +152,16 @@ namespace pcl * \return the resultant number of inliers */ virtual int - countWithinDistance (const Eigen::VectorXf &model_coefficients, - const double threshold); + countWithinDistance (const Eigen::VectorXf &model_coefficients, + const double threshold) const; /** \brief Compute all distances from the cloud data to a given plane model. * \param[in] model_coefficients the coefficients of a plane model that we need to compute distances to * \param[out] distances the resultant estimated distances */ - void - getDistancesToModel (const Eigen::VectorXf &model_coefficients, - std::vector &distances); + void + getDistancesToModel (const Eigen::VectorXf &model_coefficients, + std::vector &distances) const; /** \brief Return an unique id for this model (SACMODEL_PERPENDICULAR_PLANE). */ inline pcl::SacModel @@ -175,7 +175,7 @@ namespace pcl * \param[in] model_coefficients the set of model coefficients */ virtual bool - isModelValid (const Eigen::VectorXf &model_coefficients); + isModelValid (const Eigen::VectorXf &model_coefficients) const; /** \brief The axis along which we need to search for a plane perpendicular to. */ Eigen::Vector3f axis_; diff --git a/sample_consensus/include/pcl/sample_consensus/sac_model_plane.h b/sample_consensus/include/pcl/sample_consensus/sac_model_plane.h index 4ec78d91..e95d4224 100644 --- a/sample_consensus/include/pcl/sample_consensus/sac_model_plane.h +++ b/sample_consensus/include/pcl/sample_consensus/sac_model_plane.h @@ -184,17 +184,17 @@ namespace pcl * \param[in] samples the point indices found as possible good candidates for creating a valid model * \param[out] model_coefficients the resultant model coefficients */ - bool - computeModelCoefficients (const std::vector &samples, - Eigen::VectorXf &model_coefficients); + bool + computeModelCoefficients (const std::vector &samples, + Eigen::VectorXf &model_coefficients) const; /** \brief Compute all distances from the cloud data to a given plane model. * \param[in] model_coefficients the coefficients of a plane model that we need to compute distances to * \param[out] distances the resultant estimated distances */ - void - getDistancesToModel (const Eigen::VectorXf &model_coefficients, - std::vector &distances); + void + getDistancesToModel (const Eigen::VectorXf &model_coefficients, + std::vector &distances) const; /** \brief Select all the points which respect the given model coefficients as inliers. * \param[in] model_coefficients the coefficients of a plane model that we need to compute distances to @@ -213,8 +213,8 @@ namespace pcl * \return the resultant number of inliers */ virtual int - countWithinDistance (const Eigen::VectorXf &model_coefficients, - const double threshold); + countWithinDistance (const Eigen::VectorXf &model_coefficients, + const double threshold) const; /** \brief Recompute the plane coefficients using the given inlier set and return them to the user. * @note: these are the coefficients of the plane model after refinement (e.g. after SVD) @@ -222,10 +222,10 @@ namespace pcl * \param[in] model_coefficients the initial guess for the model coefficients * \param[out] optimized_coefficients the resultant recomputed coefficients after non-linear optimization */ - void - optimizeModelCoefficients (const std::vector &inliers, - const Eigen::VectorXf &model_coefficients, - Eigen::VectorXf &optimized_coefficients); + void + optimizeModelCoefficients (const std::vector &inliers, + const Eigen::VectorXf &model_coefficients, + Eigen::VectorXf &optimized_coefficients) const; /** \brief Create a new point cloud with inliers projected onto the plane model. * \param[in] inliers the data inliers that we want to project on the plane model @@ -233,21 +233,21 @@ namespace pcl * \param[out] projected_points the resultant projected points * \param[in] copy_data_fields set to true if we need to copy the other data fields */ - void - projectPoints (const std::vector &inliers, - const Eigen::VectorXf &model_coefficients, - PointCloud &projected_points, - bool copy_data_fields = true); + void + projectPoints (const std::vector &inliers, + const Eigen::VectorXf &model_coefficients, + PointCloud &projected_points, + bool copy_data_fields = true) const; /** \brief Verify whether a subset of indices verifies the given plane model coefficients. * \param[in] indices the data indices that need to be tested against the plane model * \param[in] model_coefficients the plane model coefficients * \param[in] threshold a maximum admissible distance threshold for determining the inliers from the outliers */ - bool - doSamplesVerifyModel (const std::set &indices, - const Eigen::VectorXf &model_coefficients, - const double threshold); + bool + doSamplesVerifyModel (const std::set &indices, + const Eigen::VectorXf &model_coefficients, + const double threshold) const; /** \brief Return an unique id for this model (SACMODEL_PLANE). */ inline pcl::SacModel diff --git a/sample_consensus/include/pcl/sample_consensus/sac_model_registration.h b/sample_consensus/include/pcl/sample_consensus/sac_model_registration.h index 6da4d577..5d88cca3 100644 --- a/sample_consensus/include/pcl/sample_consensus/sac_model_registration.h +++ b/sample_consensus/include/pcl/sample_consensus/sac_model_registration.h @@ -159,7 +159,7 @@ namespace pcl */ bool computeModelCoefficients (const std::vector &samples, - Eigen::VectorXf &model_coefficients); + Eigen::VectorXf &model_coefficients) const; /** \brief Compute all distances from the transformed points to their correspondences * \param[in] model_coefficients the 4x4 transformation matrix @@ -167,7 +167,7 @@ namespace pcl */ void getDistancesToModel (const Eigen::VectorXf &model_coefficients, - std::vector &distances); + std::vector &distances) const; /** \brief Select all the points which respect the given model coefficients as inliers. * \param[in] model_coefficients the 4x4 transformation matrix @@ -187,7 +187,7 @@ namespace pcl */ virtual int countWithinDistance (const Eigen::VectorXf &model_coefficients, - const double threshold); + const double threshold) const; /** \brief Recompute the 4x4 transformation using the given inlier set * \param[in] inliers the data inliers found as supporting the model @@ -197,19 +197,19 @@ namespace pcl void optimizeModelCoefficients (const std::vector &inliers, const Eigen::VectorXf &model_coefficients, - Eigen::VectorXf &optimized_coefficients); + Eigen::VectorXf &optimized_coefficients) const; void projectPoints (const std::vector &, const Eigen::VectorXf &, - PointCloud &, bool = true) + PointCloud &, bool = true) const { }; bool doSamplesVerifyModel (const std::set &, const Eigen::VectorXf &, - const double) + const double) const { return (false); } @@ -302,7 +302,7 @@ namespace pcl const std::vector &indices_src, const pcl::PointCloud &cloud_tgt, const std::vector &indices_tgt, - Eigen::VectorXf &transform); + Eigen::VectorXf &transform) const; /** \brief Compute mappings between original indices of the input_/target_ clouds. */ void diff --git a/sample_consensus/include/pcl/sample_consensus/sac_model_registration_2d.h b/sample_consensus/include/pcl/sample_consensus/sac_model_registration_2d.h index c64a5483..1268d63a 100644 --- a/sample_consensus/include/pcl/sample_consensus/sac_model_registration_2d.h +++ b/sample_consensus/include/pcl/sample_consensus/sac_model_registration_2d.h @@ -112,7 +112,7 @@ namespace pcl */ void getDistancesToModel (const Eigen::VectorXf &model_coefficients, - std::vector &distances); + std::vector &distances) const; /** \brief Select all the points which respect the given model coefficients as inliers. * \param[in] model_coefficients the 4x4 transformation matrix @@ -132,7 +132,7 @@ namespace pcl */ virtual int countWithinDistance (const Eigen::VectorXf &model_coefficients, - const double threshold); + const double threshold) const; /** \brief Set the camera projection matrix. * \param[in] projection_matrix the camera projection matrix diff --git a/sample_consensus/include/pcl/sample_consensus/sac_model_sphere.h b/sample_consensus/include/pcl/sample_consensus/sac_model_sphere.h index d386aa74..330de6df 100644 --- a/sample_consensus/include/pcl/sample_consensus/sac_model_sphere.h +++ b/sample_consensus/include/pcl/sample_consensus/sac_model_sphere.h @@ -78,8 +78,8 @@ namespace pcl * \param[in] random if true set the random seed to the current time, else set to 12345 (default: false) */ SampleConsensusModelSphere (const PointCloudConstPtr &cloud, - bool random = false) - : SampleConsensusModel (cloud, random), tmp_inliers_ () + bool random = false) + : SampleConsensusModel (cloud, random) { model_name_ = "SampleConsensusModelSphere"; sample_size_ = 4; @@ -91,10 +91,10 @@ namespace pcl * \param[in] indices a vector of point indices to be used from \a cloud * \param[in] random if true set the random seed to the current time, else set to 12345 (default: false) */ - SampleConsensusModelSphere (const PointCloudConstPtr &cloud, + SampleConsensusModelSphere (const PointCloudConstPtr &cloud, const std::vector &indices, - bool random = false) - : SampleConsensusModel (cloud, indices, random), tmp_inliers_ () + bool random = false) + : SampleConsensusModel (cloud, indices, random) { model_name_ = "SampleConsensusModelSphere"; sample_size_ = 4; @@ -108,7 +108,7 @@ namespace pcl * \param[in] source the model to copy into this */ SampleConsensusModelSphere (const SampleConsensusModelSphere &source) : - SampleConsensusModel (), tmp_inliers_ () + SampleConsensusModel () { *this = source; model_name_ = "SampleConsensusModelSphere"; @@ -121,7 +121,6 @@ namespace pcl operator = (const SampleConsensusModelSphere &source) { SampleConsensusModel::operator=(source); - tmp_inliers_ = source.tmp_inliers_; return (*this); } @@ -131,17 +130,17 @@ namespace pcl * \param[in] samples the point indices found as possible good candidates for creating a valid model * \param[out] model_coefficients the resultant model coefficients */ - bool - computeModelCoefficients (const std::vector &samples, - Eigen::VectorXf &model_coefficients); + bool + computeModelCoefficients (const std::vector &samples, + Eigen::VectorXf &model_coefficients) const; /** \brief Compute all distances from the cloud data to a given sphere model. * \param[in] model_coefficients the coefficients of a sphere model that we need to compute distances to * \param[out] distances the resultant estimated distances */ - void - getDistancesToModel (const Eigen::VectorXf &model_coefficients, - std::vector &distances); + void + getDistancesToModel (const Eigen::VectorXf &model_coefficients, + std::vector &distances) const; /** \brief Select all the points which respect the given model coefficients as inliers. * \param[in] model_coefficients the coefficients of a sphere model that we need to compute distances to @@ -160,8 +159,8 @@ namespace pcl * \return the resultant number of inliers */ virtual int - countWithinDistance (const Eigen::VectorXf &model_coefficients, - const double threshold); + countWithinDistance (const Eigen::VectorXf &model_coefficients, + const double threshold) const; /** \brief Recompute the sphere coefficients using the given inlier set and return them to the user. * @note: these are the coefficients of the sphere model after refinement (e.g. after SVD) @@ -169,10 +168,10 @@ namespace pcl * \param[in] model_coefficients the initial guess for the optimization * \param[out] optimized_coefficients the resultant recomputed coefficients after non-linear optimization */ - void - optimizeModelCoefficients (const std::vector &inliers, - const Eigen::VectorXf &model_coefficients, - Eigen::VectorXf &optimized_coefficients); + void + optimizeModelCoefficients (const std::vector &inliers, + const Eigen::VectorXf &model_coefficients, + Eigen::VectorXf &optimized_coefficients) const; /** \brief Create a new point cloud with inliers projected onto the sphere model. * \param[in] inliers the data inliers that we want to project on the sphere model @@ -181,21 +180,21 @@ namespace pcl * \param[in] copy_data_fields set to true if we need to copy the other data fields * \todo implement this. */ - void - projectPoints (const std::vector &inliers, - const Eigen::VectorXf &model_coefficients, - PointCloud &projected_points, - bool copy_data_fields = true); + void + projectPoints (const std::vector &inliers, + const Eigen::VectorXf &model_coefficients, + PointCloud &projected_points, + bool copy_data_fields = true) const; /** \brief Verify whether a subset of indices verifies the given sphere model coefficients. * \param[in] indices the data indices that need to be tested against the sphere model * \param[in] model_coefficients the sphere model coefficients * \param[in] threshold a maximum admissible distance threshold for determining the inliers from the outliers */ - bool - doSamplesVerifyModel (const std::set &indices, - const Eigen::VectorXf &model_coefficients, - const double threshold); + bool + doSamplesVerifyModel (const std::set &indices, + const Eigen::VectorXf &model_coefficients, + const double threshold) const; /** \brief Return an unique id for this model (SACMODEL_SPHERE). */ inline pcl::SacModel getModelType () const { return (SACMODEL_SPHERE); } @@ -208,7 +207,7 @@ namespace pcl * \param[in] model_coefficients the set of model coefficients */ virtual bool - isModelValid (const Eigen::VectorXf &model_coefficients) + isModelValid (const Eigen::VectorXf &model_coefficients) const { if (!SampleConsensusModel::isModelValid (model_coefficients)) return (false); @@ -229,8 +228,6 @@ namespace pcl isSampleGood(const std::vector &samples) const; private: - /** \brief Temporary pointer to a list of given indices for optimizeModelCoefficients () */ - const std::vector *tmp_inliers_; #if defined BUILD_Maintainer && defined __GNUC__ && __GNUC__ == 4 && __GNUC_MINOR__ > 3 #pragma GCC diagnostic ignored "-Weffc++" @@ -238,12 +235,11 @@ namespace pcl struct OptimizationFunctor : pcl::Functor { /** Functor constructor - * \param[in] m_data_points the number of data points to evaluate + * \param[in] indices the indices of data points to evaluate * \param[in] estimator pointer to the estimator object - * \param[in] distance distance computation function pointer */ - OptimizationFunctor (int m_data_points, pcl::SampleConsensusModelSphere *model) : - pcl::Functor(m_data_points), model_ (model) {} + OptimizationFunctor (const pcl::SampleConsensusModelSphere *model, const std::vector& indices) : + pcl::Functor (indices.size ()), model_ (model), indices_ (indices) {} /** Cost function to be minimized * \param[in] x the variables array @@ -258,17 +254,18 @@ namespace pcl for (int i = 0; i < values (); ++i) { // Compute the difference between the center of the sphere and the datapoint X_i - cen_t[0] = model_->input_->points[(*model_->tmp_inliers_)[i]].x - x[0]; - cen_t[1] = model_->input_->points[(*model_->tmp_inliers_)[i]].y - x[1]; - cen_t[2] = model_->input_->points[(*model_->tmp_inliers_)[i]].z - x[2]; - + cen_t[0] = model_->input_->points[indices_[i]].x - x[0]; + cen_t[1] = model_->input_->points[indices_[i]].y - x[1]; + cen_t[2] = model_->input_->points[indices_[i]].z - x[2]; + // g = sqrt ((x-a)^2 + (y-b)^2 + (z-c)^2) - R fvec[i] = std::sqrt (cen_t.dot (cen_t)) - x[3]; } return (0); } - - pcl::SampleConsensusModelSphere *model_; + + const pcl::SampleConsensusModelSphere *model_; + const std::vector &indices_; }; #if defined BUILD_Maintainer && defined __GNUC__ && __GNUC__ == 4 && __GNUC_MINOR__ > 3 #pragma GCC diagnostic warning "-Weffc++" diff --git a/sample_consensus/include/pcl/sample_consensus/sac_model_stick.h b/sample_consensus/include/pcl/sample_consensus/sac_model_stick.h index 5c307ff6..3755d64e 100644 --- a/sample_consensus/include/pcl/sample_consensus/sac_model_stick.h +++ b/sample_consensus/include/pcl/sample_consensus/sac_model_stick.h @@ -115,17 +115,17 @@ namespace pcl * \param[in] samples the point indices found as possible good candidates for creating a valid model * \param[out] model_coefficients the resultant model coefficients */ - bool - computeModelCoefficients (const std::vector &samples, - Eigen::VectorXf &model_coefficients); + bool + computeModelCoefficients (const std::vector &samples, + Eigen::VectorXf &model_coefficients) const; /** \brief Compute all squared distances from the cloud data to a given stick model. * \param[in] model_coefficients the coefficients of a stick model that we need to compute distances to * \param[out] distances the resultant estimated squared distances */ - void - getDistancesToModel (const Eigen::VectorXf &model_coefficients, - std::vector &distances); + void + getDistancesToModel (const Eigen::VectorXf &model_coefficients, + std::vector &distances) const; /** \brief Select all the points which respect the given model coefficients as inliers. * \param[in] model_coefficients the coefficients of a stick model that we need to compute distances to @@ -144,8 +144,8 @@ namespace pcl * \return the resultant number of inliers */ virtual int - countWithinDistance (const Eigen::VectorXf &model_coefficients, - const double threshold); + countWithinDistance (const Eigen::VectorXf &model_coefficients, + const double threshold) const; /** \brief Recompute the stick coefficients using the given inlier set and return them to the user. * @note: these are the coefficients of the stick model after refinement (e.g. after SVD) @@ -153,10 +153,10 @@ namespace pcl * \param[in] model_coefficients the initial guess for the model coefficients * \param[out] optimized_coefficients the resultant recomputed coefficients after optimization */ - void - optimizeModelCoefficients (const std::vector &inliers, - const Eigen::VectorXf &model_coefficients, - Eigen::VectorXf &optimized_coefficients); + void + optimizeModelCoefficients (const std::vector &inliers, + const Eigen::VectorXf &model_coefficients, + Eigen::VectorXf &optimized_coefficients) const; /** \brief Create a new point cloud with inliers projected onto the stick model. * \param[in] inliers the data inliers that we want to project on the stick model @@ -164,21 +164,21 @@ namespace pcl * \param[out] projected_points the resultant projected points * \param[in] copy_data_fields set to true if we need to copy the other data fields */ - void - projectPoints (const std::vector &inliers, - const Eigen::VectorXf &model_coefficients, - PointCloud &projected_points, - bool copy_data_fields = true); + void + projectPoints (const std::vector &inliers, + const Eigen::VectorXf &model_coefficients, + PointCloud &projected_points, + bool copy_data_fields = true) const; /** \brief Verify whether a subset of indices verifies the given stick model coefficients. * \param[in] indices the data indices that need to be tested against the plane model * \param[in] model_coefficients the plane model coefficients * \param[in] threshold a maximum admissible distance threshold for determining the inliers from the outliers */ - bool - doSamplesVerifyModel (const std::set &indices, - const Eigen::VectorXf &model_coefficients, - const double threshold); + bool + doSamplesVerifyModel (const std::set &indices, + const Eigen::VectorXf &model_coefficients, + const double threshold) const; /** \brief Return an unique id for this model (SACMODEL_STICK). */ inline pcl::SacModel diff --git a/search/include/pcl/search/flann_search.h b/search/include/pcl/search/flann_search.h index 33be27f8..f411ce82 100644 --- a/search/include/pcl/search/flann_search.h +++ b/search/include/pcl/search/flann_search.h @@ -319,7 +319,7 @@ namespace pcl { point_representation_ = point_representation; dim_ = point_representation->getNumberOfDimensions (); - if (input_) // re-create the tree, since point_represenation might change things such as the scaling of the point clouds. + if (input_) // re-create the tree, since point_representation might change things such as the scaling of the point clouds. setInputCloud (input_, indices_); } diff --git a/search/include/pcl/search/impl/organized.hpp b/search/include/pcl/search/impl/organized.hpp index 15f80724..ba79e38f 100644 --- a/search/include/pcl/search/impl/organized.hpp +++ b/search/include/pcl/search/impl/organized.hpp @@ -153,7 +153,7 @@ pcl::search::OrganizedNeighbor::nearestKSearch (const PointT &query, testPoint (query, k, results, yBegin * input_->width + xBegin); else // point lys { - // find the box that touches the image border -> dont waste time evaluating boxes that are completely outside the image! + // find the box that touches the image border -> don't waste time evaluating boxes that are completely outside the image! int dist = std::numeric_limits::max (); if (xBegin < 0) diff --git a/search/include/pcl/search/organized.h b/search/include/pcl/search/organized.h index 6f1790a8..2f128bd8 100644 --- a/search/include/pcl/search/organized.h +++ b/search/include/pcl/search/organized.h @@ -208,7 +208,7 @@ namespace pcl /** \brief test if point given by index is among the k NN in results to the query point. * \param[in] query query point * \param[in] k number of maximum nn interested in - * \param[in] queue priority queue with k NN + * \param[in,out] queue priority queue with k NN * \param[in] index index on point to be tested * \return whether the top element changed or not. */ @@ -224,7 +224,10 @@ namespace pcl float dist_z = point.z - query.z; float squared_distance = dist_x * dist_x + dist_y * dist_y + dist_z * dist_z; if (queue.size () < k) + { queue.push (Entry (index, squared_distance)); + return queue.size () == k; + } else if (queue.top ().distance > squared_distance) { queue.pop (); diff --git a/segmentation/include/pcl/segmentation/comparator.h b/segmentation/include/pcl/segmentation/comparator.h index 79b76f13..ad12d3c1 100644 --- a/segmentation/include/pcl/segmentation/comparator.h +++ b/segmentation/include/pcl/segmentation/comparator.h @@ -97,6 +97,8 @@ namespace pcl protected: PointCloudConstPtr input_; + public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW }; } diff --git a/segmentation/include/pcl/segmentation/conditional_euclidean_clustering.h b/segmentation/include/pcl/segmentation/conditional_euclidean_clustering.h index 75fa422e..eac5e3be 100644 --- a/segmentation/include/pcl/segmentation/conditional_euclidean_clustering.h +++ b/segmentation/include/pcl/segmentation/conditional_euclidean_clustering.h @@ -106,6 +106,23 @@ namespace pcl { } + /** \brief Provide a pointer to the search object. + * \param[in] tree a pointer to the spatial search object. + */ + inline void + setSearchMethod (const SearcherPtr &tree) + { + searcher_ = tree; + } + + /** \brief Get a pointer to the search method used. + */ + inline const SearcherPtr& + getSearchMethod () const + { + return searcher_; + } + /** \brief Set the condition that needs to hold for neighboring points to be considered part of the same cluster. * \details Any two points within a certain distance from one another will need to evaluate this condition in order to be made part of the same cluster. * The distance can be set using setClusterTolerance(). diff --git a/segmentation/include/pcl/segmentation/cpc_segmentation.h b/segmentation/include/pcl/segmentation/cpc_segmentation.h index 70a20932..cc10277f 100644 --- a/segmentation/include/pcl/segmentation/cpc_segmentation.h +++ b/segmentation/include/pcl/segmentation/cpc_segmentation.h @@ -157,7 +157,7 @@ namespace pcl /** \brief Use clean cutting */ bool use_clean_cutting_; - /** \brief Interations for RANSAC */ + /** \brief Iterations for RANSAC */ uint32_t ransac_itrs_; diff --git a/segmentation/include/pcl/segmentation/euclidean_cluster_comparator.h b/segmentation/include/pcl/segmentation/euclidean_cluster_comparator.h index 7cff39fc..d7922308 100644 --- a/segmentation/include/pcl/segmentation/euclidean_cluster_comparator.h +++ b/segmentation/include/pcl/segmentation/euclidean_cluster_comparator.h @@ -42,169 +42,240 @@ #include #include +#include namespace pcl { - /** \brief EuclideanClusterComparator is a comparator used for finding clusters supported by planar surfaces. - * This needs to be run as a second pass after extracting planar surfaces, using MultiPlaneSegmentation for example. + namespace experimental + { + template + class EuclideanClusterComparator : public ::pcl::Comparator + { + protected: + + using pcl::Comparator::input_; + + public: + using typename Comparator::PointCloud; + using typename Comparator::PointCloudConstPtr; + + typedef typename pcl::PointCloud PointCloudL; + typedef typename PointCloudL::Ptr PointCloudLPtr; + typedef typename PointCloudL::ConstPtr PointCloudLConstPtr; + + typedef boost::shared_ptr > Ptr; + typedef boost::shared_ptr > ConstPtr; + + typedef std::set ExcludeLabelSet; + typedef boost::shared_ptr ExcludeLabelSetPtr; + typedef boost::shared_ptr ExcludeLabelSetConstPtr; + + /** \brief Default constructor for EuclideanClusterComparator. */ + EuclideanClusterComparator () + : distance_threshold_ (0.005f) + , depth_dependent_ () + , z_axis_ () + {} + + virtual void + setInputCloud (const PointCloudConstPtr& cloud) + { + input_ = cloud; + Eigen::Matrix3f rot = input_->sensor_orientation_.toRotationMatrix (); + z_axis_ = rot.col (2); + } + + /** \brief Set the tolerance in meters for difference in perpendicular distance (d component of plane equation) to the plane between neighboring points, to be considered part of the same plane. + * \param[in] distance_threshold the tolerance in meters + * \param depth_dependent + */ + inline void + setDistanceThreshold (float distance_threshold, bool depth_dependent) + { + distance_threshold_ = distance_threshold; + depth_dependent_ = depth_dependent; + } + + /** \brief Get the distance threshold in meters (d component of plane equation) between neighboring points, to be considered part of the same plane. */ + inline float + getDistanceThreshold () const + { + return (distance_threshold_); + } + + /** \brief Set label cloud + * \param[in] labels The label cloud + */ + void + setLabels (const PointCloudLPtr& labels) + { + labels_ = labels; + } + + const ExcludeLabelSetConstPtr& + getExcludeLabels () const + { + return exclude_labels_; + } + + /** \brief Set labels in the label cloud to exclude. + * \param exclude_labels a vector of bools corresponding to whether or not a given label should be considered + */ + void + setExcludeLabels (const ExcludeLabelSetConstPtr &exclude_labels) + { + exclude_labels_ = exclude_labels; + } + + /** \brief Compare points at two indices by their euclidean distance + * \param idx1 The first index for the comparison + * \param idx2 The second index for the comparison + */ + virtual bool + compare (int idx1, int idx2) const + { + if (labels_ && exclude_labels_) + { + assert (labels_->size () == input_->size ()); + const uint32_t &label1 = (*labels_)[idx1].label; + const uint32_t &label2 = (*labels_)[idx2].label; + + const std::set::const_iterator it1 = exclude_labels_->find (label1); + if (it1 == exclude_labels_->end ()) + return false; + + const std::set::const_iterator it2 = exclude_labels_->find (label2); + if (it2 == exclude_labels_->end ()) + return false; + } + + float dist_threshold = distance_threshold_; + if (depth_dependent_) + { + Eigen::Vector3f vec = input_->points[idx1].getVector3fMap (); + float z = vec.dot (z_axis_); + dist_threshold *= z * z; + } + + const float dist = ((*input_)[idx1].getVector3fMap () + - (*input_)[idx2].getVector3fMap ()).norm (); + return (dist < dist_threshold); + } + + protected: + + + /** \brief Set of labels with similar size as the input point cloud, + * aggregating points into groups based on a similar label identifier. + * + * It needs to be set in conjunction with the \ref exclude_labels_ + * member in order to provided a masking functionality. + */ + PointCloudLPtr labels_; + + /** \brief Specifies which labels should be excluded com being clustered. + * + * If a label is not specified, it's assumed by default that it's + * intended be excluded + */ + ExcludeLabelSetConstPtr exclude_labels_; + + float distance_threshold_; + + bool depth_dependent_; + + Eigen::Vector3f z_axis_; + }; + } // namespace experimental + + + /** \brief EuclideanClusterComparator is a comparator used for finding clusters based on euclidian distance. * * \author Alex Trevor */ - template - class EuclideanClusterComparator: public Comparator + template + class EuclideanClusterComparator : public experimental::EuclideanClusterComparator { + protected: + + using experimental::EuclideanClusterComparator::exclude_labels_; + public: - typedef typename Comparator::PointCloud PointCloud; - typedef typename Comparator::PointCloudConstPtr PointCloudConstPtr; - + typedef typename pcl::PointCloud PointCloudN; typedef typename PointCloudN::Ptr PointCloudNPtr; typedef typename PointCloudN::ConstPtr PointCloudNConstPtr; - - typedef typename pcl::PointCloud PointCloudL; - typedef typename PointCloudL::Ptr PointCloudLPtr; - typedef typename PointCloudL::ConstPtr PointCloudLConstPtr; typedef boost::shared_ptr > Ptr; typedef boost::shared_ptr > ConstPtr; - using pcl::Comparator::input_; - - /** \brief Empty constructor for EuclideanClusterComparator. */ + using experimental::EuclideanClusterComparator::setExcludeLabels; + + /** \brief Default constructor for EuclideanClusterComparator. */ + PCL_DEPRECATED ("Remove PointNT from template parameters.") EuclideanClusterComparator () : normals_ () , angular_threshold_ (0.0f) - , distance_threshold_ (0.005f) - , depth_dependent_ () - , z_axis_ () - { - } - - /** \brief Destructor for EuclideanClusterComparator. */ - virtual - ~EuclideanClusterComparator () - { - } + {} - virtual void - setInputCloud (const PointCloudConstPtr& cloud) - { - input_ = cloud; - Eigen::Matrix3f rot = input_->sensor_orientation_.toRotationMatrix (); - z_axis_ = rot.col (2); - } - /** \brief Provide a pointer to the input normals. - * \param[in] normals the input normal cloud - */ + * \param[in] normals the input normal cloud + */ inline void - setInputNormals (const PointCloudNConstPtr &normals) - { - normals_ = normals; - } + PCL_DEPRECATED ("EuclideadClusterComparator never actually used normals and angular threshold, " + "this function has no effect on the behavior of the comparator. Therefore it is " + "deprecated and will be removed in future releases.") + setInputNormals (const PointCloudNConstPtr& normals) { normals_ = normals; } /** \brief Get the input normals. */ inline PointCloudNConstPtr - getInputNormals () const - { - return (normals_); - } + PCL_DEPRECATED ("EuclideadClusterComparator never actually used normals and angular threshold, " + "this function has no effect on the behavior of the comparator. Therefore it is " + "deprecated and will be removed in future releases.") + getInputNormals () const { return (normals_); } /** \brief Set the tolerance in radians for difference in normal direction between neighboring points, to be considered part of the same plane. * \param[in] angular_threshold the tolerance in radians */ - virtual inline void - setAngularThreshold (float angular_threshold) - { - angular_threshold_ = cosf (angular_threshold); - } - - /** \brief Get the angular threshold in radians for difference in normal direction between neighboring points, to be considered part of the same plane. */ - inline float - getAngularThreshold () const - { - return (acos (angular_threshold_) ); - } - - /** \brief Set the tolerance in meters for difference in perpendicular distance (d component of plane equation) to the plane between neighboring points, to be considered part of the same plane. - * \param[in] distance_threshold the tolerance in meters - * \param depth_dependent - */ inline void - setDistanceThreshold (float distance_threshold, bool depth_dependent) + PCL_DEPRECATED ("EuclideadClusterComparator never actually used normals and angular threshold, " + "this function has no effect on the behavior of the comparator. Therefore it is " + "deprecated and will be removed in future releases.") + setAngularThreshold (float angular_threshold) { - distance_threshold_ = distance_threshold; - depth_dependent_ = depth_dependent; + angular_threshold_ = std::cos (angular_threshold); } - /** \brief Get the distance threshold in meters (d component of plane equation) between neighboring points, to be considered part of the same plane. */ + /** \brief Get the angular threshold in radians for difference in normal direction between neighboring points, to be considered part of the same plane. */ inline float - getDistanceThreshold () const - { - return (distance_threshold_); - } - - /** \brief Set label cloud - * \param[in] labels The label cloud - */ - void - setLabels (PointCloudLPtr& labels) - { - labels_ = labels; - } + PCL_DEPRECATED ("EuclideadClusterComparator never actually used normals and angular threshold, " + "this function has no effect on the behavior of the comparator. Therefore it is " + "deprecated and will be removed in future releases.") + getAngularThreshold () const { return (std::acos (angular_threshold_) ); } /** \brief Set labels in the label cloud to exclude. - * \param exclude_labels a vector of bools corresponding to whether or not a given label should be considered + * \param[in] exclude_labels a vector of bools corresponding to whether or not a given label should be considered */ void - setExcludeLabels (std::vector& exclude_labels) + PCL_DEPRECATED ("Use setExcludeLabels (const ExcludeLabelSetConstPtr &) instead") + setExcludeLabels (const std::vector& exclude_labels) { - exclude_labels_ = boost::make_shared >(exclude_labels); + exclude_labels_ = boost::make_shared > (); + for (uint32_t i = 0; i < exclude_labels.size (); ++i) + if (exclude_labels[i]) + exclude_labels_->insert (i); } - /** \brief Compare points at two indices by their plane equations. True if the angle between the normals is less than the angular threshold, - * and the difference between the d component of the normals is less than distance threshold, else false - * \param idx1 The first index for the comparison - * \param idx2 The second index for the comparison - */ - virtual bool - compare (int idx1, int idx2) const - { - int label1 = labels_->points[idx1].label; - int label2 = labels_->points[idx2].label; - - if (label1 == -1 || label2 == -1) - return false; - - if ( (*exclude_labels_)[label1] || (*exclude_labels_)[label2]) - return false; - - float dist_threshold = distance_threshold_; - if (depth_dependent_) - { - Eigen::Vector3f vec = input_->points[idx1].getVector3fMap (); - float z = vec.dot (z_axis_); - dist_threshold *= z * z; - } - - float dx = input_->points[idx1].x - input_->points[idx2].x; - float dy = input_->points[idx1].y - input_->points[idx2].y; - float dz = input_->points[idx1].z - input_->points[idx2].z; - float dist = std::sqrt (dx*dx + dy*dy + dz*dz); - - return (dist < dist_threshold); - } - protected: + PointCloudNConstPtr normals_; - PointCloudLPtr labels_; - boost::shared_ptr > exclude_labels_; float angular_threshold_; - float distance_threshold_; - bool depth_dependent_; - Eigen::Vector3f z_axis_; }; + + template + class EuclideanClusterComparator + : public experimental::EuclideanClusterComparator {}; } #endif // PCL_SEGMENTATION_PLANE_COEFFICIENT_COMPARATOR_H_ diff --git a/segmentation/include/pcl/segmentation/grabcut_segmentation.h b/segmentation/include/pcl/segmentation/grabcut_segmentation.h index 378bc022..7190a3bd 100644 --- a/segmentation/include/pcl/segmentation/grabcut_segmentation.h +++ b/segmentation/include/pcl/segmentation/grabcut_segmentation.h @@ -53,7 +53,7 @@ namespace pcl namespace grabcut { /** boost implementation of Boykov and Kolmogorov's maxflow algorithm doesn't support - * negative flows which makes it inappropriate for this conext. + * negative flows which makes it inappropriate for this context. * This implementation of Boykov and Kolmogorov's maxflow algorithm by Stephen Gould * in DARWIN under BSD does the trick however solwer than original * implementation. @@ -195,7 +195,7 @@ namespace pcl colorDistance (const Color& c1, const Color& c2); /// User supplied Trimap values enum TrimapValue { TrimapUnknown = -1, TrimapForeground, TrimapBackground }; - /// Grabcut derived hard segementation values + /// Grabcut derived hard segmentation values enum SegmentationValue { SegmentationForeground = 0, SegmentationBackground }; /// Gaussian structure struct Gaussian @@ -211,9 +211,9 @@ namespace pcl Eigen::Matrix3f inverse; /// weighting of this gaussian in the GMM. float pi; - /// heighest eigenvalue of covariance matrix + /// highest eigenvalue of covariance matrix float eigenvalue; - /// eigenvector corresponding to the heighest eigenvector + /// eigenvector corresponding to the highest eigenvector Eigen::Vector3f eigenvector; }; @@ -286,6 +286,7 @@ namespace pcl uint32_t count_; /// small value to add to covariance matrix diagonal to avoid singular values float epsilon_; + EIGEN_MAKE_ALIGNED_OPERATOR_NEW }; /** Build the initial GMMs using the Orchard and Bouman color clustering algorithm */ @@ -332,7 +333,7 @@ namespace pcl , nb_neighbours_ (9) , initialized_ (false) {} - /// Desctructor + /// Destructor virtual ~GrabCut () {}; // /// Set input cloud void diff --git a/segmentation/include/pcl/segmentation/ground_plane_comparator.h b/segmentation/include/pcl/segmentation/ground_plane_comparator.h index e39354d7..f96f38bf 100644 --- a/segmentation/include/pcl/segmentation/ground_plane_comparator.h +++ b/segmentation/include/pcl/segmentation/ground_plane_comparator.h @@ -147,7 +147,7 @@ namespace pcl const std::vector& getPlaneCoeffD () const { - return (plane_coeff_d_); + return (*plane_coeff_d_); } /** \brief Set the tolerance in radians for difference in normal direction between neighboring points, to be considered part of the same plane. diff --git a/segmentation/include/pcl/segmentation/impl/cpc_segmentation.hpp b/segmentation/include/pcl/segmentation/impl/cpc_segmentation.hpp index 30f789b8..4bdae7f1 100644 --- a/segmentation/include/pcl/segmentation/impl/cpc_segmentation.hpp +++ b/segmentation/include/pcl/segmentation/impl/cpc_segmentation.hpp @@ -68,7 +68,7 @@ pcl::CPCSegmentation::segment () // Correct edge relations using extended convexity definition if k>0 applyKconvexity (k_factor_); - // Determine wether to use cutting planes + // Determine whether to use cutting planes doGrouping (); grouping_data_valid_ = true; @@ -98,7 +98,7 @@ pcl::CPCSegmentation::applyCuttingPlane (uint32_t depth_levels_left) boost::tie (edge_itr, edge_itr_end) = boost::edges (sv_adjacency_list_); for (next_edge = edge_itr; edge_itr != edge_itr_end; edge_itr = next_edge) { - next_edge++; // next_edge iterator is neccessary, because removing an edge invalidates the iterator to the current edge + next_edge++; // next_edge iterator is necessary, because removing an edge invalidates the iterator to the current edge uint32_t source_sv_label = sv_adjacency_list_[boost::source (*edge_itr, sv_adjacency_list_)]; uint32_t target_sv_label = sv_adjacency_list_[boost::target (*edge_itr, sv_adjacency_list_)]; @@ -182,8 +182,8 @@ pcl::CPCSegmentation::applyCuttingPlane (uint32_t depth_levels_left) { Eigen::Vector3f plane_normal (model_coefficients[0], model_coefficients[1], model_coefficients[2]); // Cut the connections. - // We only interate through the points which are within the support (when we are local, otherwise all points in the segment). - // We also just acutally cut when the edge goes through the plane. This is why we check the planedistance + // We only iterate through the points which are within the support (when we are local, otherwise all points in the segment). + // We also just actually cut when the edge goes through the plane. This is why we check the planedistance std::vector cluster_indices; pcl::EuclideanClusterExtraction euclidean_clusterer; pcl::search::KdTree::Ptr tree (new pcl::search::KdTree); @@ -225,7 +225,7 @@ pcl::CPCSegmentation::applyCuttingPlane (uint32_t depth_levels_left) } if (cut_support_indices.size () == 0) { -// std::cout << "Could not find planes which exceed required minumum score (threshold " << min_cut_score_ << "), not cutting" << std::endl; +// std::cout << "Could not find planes which exceed required minimum score (threshold " << min_cut_score_ << "), not cutting" << std::endl; continue; } } @@ -303,7 +303,7 @@ pcl::CPCSegmentation::WeightedRandomSampleConsensus::computeModel (int) Eigen::VectorXf model_coefficients; unsigned skipped_count = 0; - // supress infinite loops by just allowing 10 x maximum allowed iterations for invalid model parameters! + // suppress infinite loops by just allowing 10 x maximum allowed iterations for invalid model parameters! const unsigned max_skip = max_iterations_ * 10; // Iterate diff --git a/segmentation/include/pcl/segmentation/impl/lccp_segmentation.hpp b/segmentation/include/pcl/segmentation/impl/lccp_segmentation.hpp index 34f55b5b..72b6eec6 100644 --- a/segmentation/include/pcl/segmentation/impl/lccp_segmentation.hpp +++ b/segmentation/include/pcl/segmentation/impl/lccp_segmentation.hpp @@ -248,7 +248,7 @@ pcl::LCCPSegmentation::mergeSmallSegments () // After filtered Segments are deleted, compute completely new adjacency map // NOTE Recomputing the adjacency of every segment in every iteration is an easy but inefficient solution. - // Because the number of segments in an average scene is usually well below 1000, the time spend for noise filtering is still neglible in most cases + // Because the number of segments in an average scene is usually well below 1000, the time spend for noise filtering is still negligible in most cases computeSegmentAdjacency (); } // End while (Filtering) } @@ -349,7 +349,7 @@ pcl::LCCPSegmentation::recursiveSegmentGrowing (const VertexID &query_po sv_label_to_seg_label_map_[sv_label] = segment_label; seg_label_to_sv_list_map_[segment_label].insert (sv_label); - // Iterate through all neighbors of this supervoxel and check wether they should be merged with the current supervoxel + // Iterate through all neighbors of this supervoxel and check whether they should be merged with the current supervoxel std::pair out_edge_iterator_range; out_edge_iterator_range = boost::out_edges (query_point_id, sv_adjacency_list_); // adjacent vertices to node (*itr) in graph sv_adjacency_list_ for (OutEdgeIterator out_Edge_itr = out_edge_iterator_range.first; out_Edge_itr != out_edge_iterator_range.second; ++out_Edge_itr) @@ -385,7 +385,7 @@ pcl::LCCPSegmentation::applyKconvexity (const unsigned int k_arg) // Check all edges in the graph for k-convexity for (next_edge = edge_itr; edge_itr != edge_itr_end; edge_itr = next_edge) { - next_edge++; // next_edge iterator is neccessary, because removing an edge invalidates the iterator to the current edge + next_edge++; // next_edge iterator is necessary, because removing an edge invalidates the iterator to the current edge is_convex = sv_adjacency_list_[*edge_itr].is_convex; @@ -443,7 +443,7 @@ pcl::LCCPSegmentation::calculateConvexConnections (SupervoxelAdjacencyLi for (next_edge = edge_itr; edge_itr != edge_itr_end; edge_itr = next_edge) { - next_edge++; // next_edge iterator is neccessary, because removing an edge invalidates the iterator to the current edge + next_edge++; // next_edge iterator is necessary, because removing an edge invalidates the iterator to the current edge uint32_t source_sv_label = adjacency_list_arg[boost::source (*edge_itr, adjacency_list_arg)]; uint32_t target_sv_label = adjacency_list_arg[boost::target (*edge_itr, adjacency_list_arg)]; diff --git a/segmentation/include/pcl/segmentation/impl/organized_connected_component_segmentation.hpp b/segmentation/include/pcl/segmentation/impl/organized_connected_component_segmentation.hpp index b0fb6ca8..71112e44 100644 --- a/segmentation/include/pcl/segmentation/impl/organized_connected_component_segmentation.hpp +++ b/segmentation/include/pcl/segmentation/impl/organized_connected_component_segmentation.hpp @@ -68,7 +68,7 @@ pcl::OrganizedConnectedComponentSegmentation::findLabeledRegion Neighbor( 0, 1, labels->width ), Neighbor(-1, 1, labels->width - 1)}; - // find one pixel with other label in the neighborhood -> assume thats the one we came from + // find one pixel with other label in the neighborhood -> assume that's the one we came from int direction = -1; int x; int y; diff --git a/segmentation/include/pcl/segmentation/impl/organized_multi_plane_segmentation.hpp b/segmentation/include/pcl/segmentation/impl/organized_multi_plane_segmentation.hpp index fadd1795..bfe32493 100644 --- a/segmentation/include/pcl/segmentation/impl/organized_multi_plane_segmentation.hpp +++ b/segmentation/include/pcl/segmentation/impl/organized_multi_plane_segmentation.hpp @@ -317,7 +317,7 @@ pcl::OrganizedMultiPlaneSegmentation::refine (std::vec PointCloudLPtr& labels, std::vector& label_indices) { - //List of lables to grow, and index of model corresponding to each label + //List of labels to grow, and index of model corresponding to each label std::vector grow_labels; std::vector label_to_model; grow_labels.resize (label_indices.size (), false); diff --git a/segmentation/include/pcl/segmentation/impl/region_growing_rgb.hpp b/segmentation/include/pcl/segmentation/impl/region_growing_rgb.hpp index a49fa681..a78cb1cc 100644 --- a/segmentation/include/pcl/segmentation/impl/region_growing_rgb.hpp +++ b/segmentation/include/pcl/segmentation/impl/region_growing_rgb.hpp @@ -317,12 +317,12 @@ pcl::RegionGrowingRGB::findRegionsKNN (int index, int nghbr_num distances.resize (clusters_.size (), max_dist); int number_of_points = num_pts_in_segment_[index]; - //loop throug every point in this segment and check neighbours + //loop through every point in this segment and check neighbours for (int i_point = 0; i_point < number_of_points; i_point++) { int point_index = clusters_[index].indices[i_point]; int number_of_neighbours = static_cast (point_neighbours_[point_index].size ()); - //loop throug every neighbour of the current point, find out to which segment it belongs + //loop through every neighbour of the current point, find out to which segment it belongs //and if it belongs to neighbouring segment and is close enough then remember segment and its distance for (int i_nghbr = 0; i_nghbr < number_of_neighbours; i_nghbr++) { diff --git a/segmentation/include/pcl/segmentation/impl/segment_differences.hpp b/segmentation/include/pcl/segmentation/impl/segment_differences.hpp old mode 100644 new mode 100755 index 2c206f0e..1c0e4eb8 --- a/segmentation/include/pcl/segmentation/impl/segment_differences.hpp +++ b/segmentation/include/pcl/segmentation/impl/segment_differences.hpp @@ -44,21 +44,22 @@ ////////////////////////////////////////////////////////////////////////// template void pcl::getPointCloudDifference ( - const pcl::PointCloud &src, - const pcl::PointCloud &, - double threshold, const boost::shared_ptr > &tree, + const pcl::PointCloud &src, + double threshold, + const boost::shared_ptr > &tree, pcl::PointCloud &output) { // We're interested in a single nearest neighbor only std::vector nn_indices (1); std::vector nn_distances (1); - // The src indices that do not have a neighbor in tgt + // The input cloud indices that do not have a neighbor in the target cloud std::vector src_indices; // Iterate through the source data set for (int i = 0; i < static_cast (src.points.size ()); ++i) { + // Ignore invalid points in the inpout cloud if (!isFinite (src.points[i])) continue; // Search for the closest point in the target data set (number of neighbors to find = 1) @@ -67,25 +68,16 @@ pcl::getPointCloudDifference ( PCL_WARN ("No neighbor found for point %lu (%f %f %f)!\n", i, src.points[i].x, src.points[i].y, src.points[i].z); continue; } - + // Add points without a corresponding point in the target cloud to the output cloud if (nn_distances[0] > threshold) src_indices.push_back (i); } - - // Allocate enough space and copy the basics - output.points.resize (src_indices.size ()); - output.header = src.header; - output.width = static_cast (src_indices.size ()); - output.height = 1; - //if (src.is_dense) - output.is_dense = true; - //else - // It's not necessarily true that is_dense is false if cloud_in.is_dense is false - // To verify this, we would need to iterate over all points and check for NaNs - //output.is_dense = false; // Copy all the data fields from the input cloud to the output one copyPointCloud (src, src_indices, output); + + // Output is always dense, as invalid points in the input cloud are ignored + output.is_dense = true; } ////////////////////////////////////////////////////////////////////////// @@ -121,13 +113,12 @@ pcl::SegmentDifferences::segment (PointCloud &output) // Send the input dataset to the spatial locator tree_->setInputCloud (target_); - getPointCloudDifference (*input_, *target_, distance_threshold_, tree_, output); + getPointCloudDifference (*input_, distance_threshold_, tree_, output); deinitCompute (); } #define PCL_INSTANTIATE_SegmentDifferences(T) template class PCL_EXPORTS pcl::SegmentDifferences; -#define PCL_INSTANTIATE_getPointCloudDifference(T) template PCL_EXPORTS void pcl::getPointCloudDifference(const pcl::PointCloud &, const pcl::PointCloud &, double, const boost::shared_ptr > &, pcl::PointCloud &); +#define PCL_INSTANTIATE_getPointCloudDifference(T) template PCL_EXPORTS void pcl::getPointCloudDifference(const pcl::PointCloud &, double, const boost::shared_ptr > &, pcl::PointCloud &); #endif // PCL_SEGMENTATION_IMPL_SEGMENT_DIFFERENCES_H_ - diff --git a/segmentation/include/pcl/segmentation/lccp_segmentation.h b/segmentation/include/pcl/segmentation/lccp_segmentation.h index b3887e76..bcba0279 100644 --- a/segmentation/include/pcl/segmentation/lccp_segmentation.h +++ b/segmentation/include/pcl/segmentation/lccp_segmentation.h @@ -61,10 +61,10 @@ namespace pcl /** \brief Edge Properties stored in the adjacency graph.*/ struct EdgeProperties { - /** \brief Desribes the difference of normals of the two supervoxels being connected*/ + /** \brief Describes the difference of normals of the two supervoxels being connected*/ float normal_difference; - /** \brief Desribes if a connection is convex or concave*/ + /** \brief Describes if a connection is convex or concave*/ bool is_convex; /** \brief Describes if a connection is valid for the segment growing. Usually convex connections are and concave connection are not. Due to k-concavity a convex connection can be invalidated*/ @@ -301,7 +301,7 @@ namespace pcl /** \brief Normal Threshold in degrees [0,180] used for merging */ float concavity_tolerance_threshold_; - /** \brief Marks if valid grouping data (\ref sv_adjacency_list_, \ref sv_label_to_seg_label_map_, \ref processed_) is avaiable */ + /** \brief Marks if valid grouping data (\ref sv_adjacency_list_, \ref sv_label_to_seg_label_map_, \ref processed_) is available */ bool grouping_data_valid_; /** \brief Marks if supervoxels have been set by calling \ref setInputSupervoxels */ diff --git a/segmentation/include/pcl/segmentation/organized_connected_component_segmentation.h b/segmentation/include/pcl/segmentation/organized_connected_component_segmentation.h index a02a3c0a..d5ab2304 100644 --- a/segmentation/include/pcl/segmentation/organized_connected_component_segmentation.h +++ b/segmentation/include/pcl/segmentation/organized_connected_component_segmentation.h @@ -111,7 +111,7 @@ namespace pcl segment (pcl::PointCloud& labels, std::vector& label_indices) const; /** \brief Find the boundary points / contour of a connected component - * \param[in] start_idx the first (lowest) index of the connected component for which a boundary shoudl be returned + * \param[in] start_idx the first (lowest) index of the connected component for which a boundary should be returned * \param[in] labels the Label cloud produced by segmentation * \param[out] boundary_indices the indices of the boundary points for the label corresponding to start_idx */ diff --git a/segmentation/include/pcl/segmentation/plane_coefficient_comparator.h b/segmentation/include/pcl/segmentation/plane_coefficient_comparator.h index 9c948137..a21725a4 100644 --- a/segmentation/include/pcl/segmentation/plane_coefficient_comparator.h +++ b/segmentation/include/pcl/segmentation/plane_coefficient_comparator.h @@ -141,7 +141,7 @@ namespace pcl const std::vector& getPlaneCoeffD () const { - return (plane_coeff_d_); + return (*plane_coeff_d_); } /** \brief Set the tolerance in radians for difference in normal direction between neighboring points, to be considered part of the same plane. diff --git a/segmentation/include/pcl/segmentation/sac_segmentation.h b/segmentation/include/pcl/segmentation/sac_segmentation.h index 41bc27d5..c5e28e9b 100644 --- a/segmentation/include/pcl/segmentation/sac_segmentation.h +++ b/segmentation/include/pcl/segmentation/sac_segmentation.h @@ -370,7 +370,7 @@ namespace pcl getNormalDistanceWeight () const { return (distance_weight_); } /** \brief Set the minimum opning angle for a cone model. - * \param min_angle the opening angle which we need minumum to validate a cone model. + * \param min_angle the opening angle which we need minimum to validate a cone model. * \param max_angle the opening angle which we need maximum to validate a cone model. */ inline void @@ -380,7 +380,7 @@ namespace pcl max_angle_ = max_angle; } - /** \brief Get the opening angle which we need minumum to validate a cone model. */ + /** \brief Get the opening angle which we need minimum to validate a cone model. */ inline void getMinMaxOpeningAngle (double &min_angle, double &max_angle) { diff --git a/segmentation/include/pcl/segmentation/segment_differences.h b/segmentation/include/pcl/segmentation/segment_differences.h old mode 100644 new mode 100755 index 316d0649..cd1b95e3 --- a/segmentation/include/pcl/segmentation/segment_differences.h +++ b/segmentation/include/pcl/segmentation/segment_differences.h @@ -46,19 +46,31 @@ namespace pcl //////////////////////////////////////////////////////////////////////////////////////////// /** \brief Obtain the difference between two aligned point clouds as another point cloud, given a distance threshold. * \param src the input point cloud source - * \param tgt the input point cloud target we need to obtain the difference against * \param threshold the distance threshold (tolerance) for point correspondences. (e.g., check if f a point p1 from * src has a correspondence > threshold than a point p2 from tgt) - * \param tree the spatial locator (e.g., kd-tree) used for nearest neighbors searching built over \a tgt + * \param tree the spatial locator (e.g., kd-tree) used for nearest neighbors searching built over the target cloud * \param output the resultant output point cloud difference * \ingroup segmentation */ template void getPointCloudDifference ( - const pcl::PointCloud &src, const pcl::PointCloud &tgt, - double threshold, const boost::shared_ptr > &tree, + const pcl::PointCloud &src, + double threshold, + const boost::shared_ptr > &tree, pcl::PointCloud &output); + template + PCL_DEPRECATED("getPointCloudDifference() does not use the tgt parameter, thus it is deprecated and will be removed in future releases.") + inline void getPointCloudDifference ( + const pcl::PointCloud &src, + const pcl::PointCloud & /* tgt */, + double threshold, + const boost::shared_ptr > &tree, + pcl::PointCloud &output) + { + getPointCloudDifference (src, threshold, tree, output); + } + //////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// diff --git a/segmentation/include/pcl/segmentation/supervoxel_clustering.h b/segmentation/include/pcl/segmentation/supervoxel_clustering.h index 1048fe59..05e60027 100644 --- a/segmentation/include/pcl/segmentation/supervoxel_clustering.h +++ b/segmentation/include/pcl/segmentation/supervoxel_clustering.h @@ -272,7 +272,7 @@ namespace pcl * Points that belong to the same supervoxel have the same color. * But this function doesn't guarantee that different segments will have different * color(it's random). Points that are unlabeled will be black - * \note This will expand the label_colors_ vector so that it can accomodate all labels + * \note This will expand the label_colors_ vector so that it can accommodate all labels */ PCL_DEPRECATED ("SupervoxelClustering::getColoredCloud is deprecated. Use the getLabeledCloud function instead. examples/segmentation/example_supervoxels.cpp shows how to use this to display and save with colorized labels.") typename pcl::PointCloud::Ptr @@ -297,7 +297,7 @@ namespace pcl * Points that belong to the same supervoxel have the same color. * But this function doesn't guarantee that different segments will have different * color(it's random). Points that are unlabeled will be black - * \note This will expand the label_colors_ vector so that it can accomodate all labels + * \note This will expand the label_colors_ vector so that it can accommodate all labels */ PCL_DEPRECATED ("SupervoxelClustering::getColoredVoxelCloud is deprecated. Use the getLabeledVoxelCloud function instead. examples/segmentation/example_supervoxels.cpp shows how to use this to display and save with colorized labels.") pcl::PointCloud::Ptr diff --git a/segmentation/src/supervoxel_clustering.cpp b/segmentation/src/supervoxel_clustering.cpp index da81d0b1..41cad71f 100644 --- a/segmentation/src/supervoxel_clustering.cpp +++ b/segmentation/src/supervoxel_clustering.cpp @@ -63,7 +63,7 @@ namespace pcl data_.xyz_[0] += new_point.x; data_.xyz_[1] += new_point.y; data_.xyz_[2] += new_point.z; - //Separate sums for r,g,b since we cant sum in uchars + //Separate sums for r,g,b since we can't sum in uchars data_.rgb_[0] += static_cast (new_point.r); data_.rgb_[1] += static_cast (new_point.g); data_.rgb_[2] += static_cast (new_point.b); @@ -78,7 +78,7 @@ namespace pcl data_.xyz_[0] += new_point.x; data_.xyz_[1] += new_point.y; data_.xyz_[2] += new_point.z; - //Separate sums for r,g,b since we cant sum in uchars + //Separate sums for r,g,b since we can't sum in uchars data_.rgb_[0] += static_cast (new_point.r); data_.rgb_[1] += static_cast (new_point.g); data_.rgb_[2] += static_cast (new_point.b); diff --git a/simulation/include/pcl/simulation/model.h b/simulation/include/pcl/simulation/model.h index b3ba935a..2a6b5d4b 100644 --- a/simulation/include/pcl/simulation/model.h +++ b/simulation/include/pcl/simulation/model.h @@ -58,7 +58,7 @@ namespace pcl }; typedef std::vector Vertices; - typedef std::vector Indices; + typedef std::vector Indices; class Model { diff --git a/simulation/include/pcl/simulation/range_likelihood.h b/simulation/include/pcl/simulation/range_likelihood.h index e1e37c87..39334f90 100644 --- a/simulation/include/pcl/simulation/range_likelihood.h +++ b/simulation/include/pcl/simulation/range_likelihood.h @@ -84,11 +84,11 @@ namespace pcl */ void setCameraIntrinsicsParameters (int camera_width_in, - int camera_height_in, - float camera_fx_in, - float camera_fy_in, - float camera_cx_in, - float camera_cy_in) + int camera_height_in, + float camera_fx_in, + float camera_fy_in, + float camera_cx_in, + float camera_cy_in) { camera_width_ = camera_width_in; camera_height_ = camera_height_in; @@ -98,6 +98,25 @@ namespace pcl camera_cy_ = camera_cy_in; } + /** + * Get the basic camera intrinsic parameters + */ + void + getCameraIntrinsicsParameters (int &camera_width_in, + int &camera_height_in, + float &camera_fx_in, + float &camera_fy_in, + float &camera_cx_in, + float &camera_cy_in) const + { + camera_width_in = camera_width_; + camera_height_in = camera_height_; + camera_fx_in = camera_fx_; + camera_fy_in = camera_fy_; + camera_cx_in = camera_cx_; + camera_cy_in = camera_cy_; + } + /** * Set the cost function to be used - one of 4 hard coded currently */ @@ -105,23 +124,23 @@ namespace pcl void setSigma (double sigma_in){ sigma_ = sigma_in; } void setFloorProportion (double floor_proportion_in){ floor_proportion_ = floor_proportion_in;} - int getRows () {return rows_;} - int getCols () {return cols_;} - int getRowHeight () {return row_height_;} - int getColWidth () {return col_width_;} - int getWidth () {return width_;} - int getHeight () {return height_;} + int getRows () const {return rows_;} + int getCols () const {return cols_;} + int getRowHeight () const {return row_height_;} + int getColWidth () const {return col_width_;} + int getWidth () const {return width_;} + int getHeight () const {return height_;} // Convenience function to return simulated RGB-D PointCloud // Two modes: // global=false - PointCloud is as would be captured by an RGB-D camera [default] // global=true - PointCloud is transformed into the model/world frame using the camera pose void getPointCloud (pcl::PointCloud::Ptr pc, - bool make_global, const Eigen::Isometry3d & pose); + bool make_global, const Eigen::Isometry3d & pose, bool organized = false) const; // Convenience function to return RangeImagePlanar containing // simulated RGB-D: - void getRangeImagePlanar (pcl::RangeImagePlanar &rip); + void getRangeImagePlanar (pcl::RangeImagePlanar &rip) const; // Add various types of noise to simulated RGB-D data void addNoise (); @@ -137,14 +156,26 @@ namespace pcl setUseColor(bool use_color) { use_color_ = use_color; } const uint8_t* - getColorBuffer (); + getColorBuffer () const; const float* - getDepthBuffer (); + getDepthBuffer () const; const float* - getScoreBuffer (); + getScoreBuffer () const; + float + getZNear () const { return z_near_; } + + float + getZFar () const { return z_far_; } + + void + setZNear (float z){ z_near_ = z; } + + void + setZFar (float z){ z_far_ = z; } + private: /** * Evaluate the likelihood/score for a set of particles @@ -193,13 +224,14 @@ namespace pcl float camera_cy_; // min and max range of the rgbd sensor - // everything outside this doesnt appear in depth images + // everything outside this doesn't appear in depth images float z_near_; float z_far_; - bool depth_buffer_dirty_; - bool color_buffer_dirty_; - bool score_buffer_dirty_; + // For caching only, not part of observable state. + mutable bool depth_buffer_dirty_; + mutable bool color_buffer_dirty_; + mutable bool score_buffer_dirty_; int which_cost_function_; double floor_proportion_; diff --git a/simulation/include/pcl/simulation/sum_reduce.h b/simulation/include/pcl/simulation/sum_reduce.h index 4b3ab43e..bf53ac39 100644 --- a/simulation/include/pcl/simulation/sum_reduce.h +++ b/simulation/include/pcl/simulation/sum_reduce.h @@ -26,7 +26,7 @@ namespace pcl { /** \brief Implements a parallel summation of float arrays using GLSL. * The input array is provided as a float texture and the summation - * is performed over set number of levels, where each level halfs each + * is performed over set number of levels, where each level halves each * dimension. * * \author Hordur Johannsson diff --git a/simulation/src/model.cpp b/simulation/src/model.cpp index 59f553f4..39d6f17f 100644 --- a/simulation/src/model.cpp +++ b/simulation/src/model.cpp @@ -116,7 +116,7 @@ pcl::simulation::PolygonMeshModel::PolygonMeshModel (GLenum mode, pcl::PolygonMe { bool found_rgb=false; for (size_t i=0; icloud.fields.size () ;i++) - if (plg->cloud.fields[i].name.compare ("rgb") == 0) + if (plg->cloud.fields[i].name.compare ("rgb") == 0 || plg->cloud.fields[i].name.compare ("rgba") == 0) found_rgb = true; if (found_rgb) @@ -144,7 +144,7 @@ pcl::simulation::PolygonMeshModel::PolygonMeshModel (GLenum mode, pcl::PolygonMe apoly.colors_[4*j + 0] = newcloud.points[pt].r/255.0f; // Red apoly.colors_[4*j + 1] = newcloud.points[pt].g/255.0f; // Green apoly.colors_[4*j + 2] = newcloud.points[pt].b/255.0f; // Blue - apoly.colors_[4*j + 3] = 1.0f; // transparancy? unnecessary? + apoly.colors_[4*j + 3] = 1.0f; // transparency? unnecessary? } polygons.push_back (apoly); } diff --git a/simulation/src/range_likelihood.cpp b/simulation/src/range_likelihood.cpp index 91a08766..1b05e4c6 100644 --- a/simulation/src/range_likelihood.cpp +++ b/simulation/src/range_likelihood.cpp @@ -505,7 +505,7 @@ costFunction3 (float ref_val,float depth_val) { // working range float min_dist = abs (ref_val - 0.7253f/(1.0360f - (depth_val))); - int lup = static_cast (ceil (min_dist*100)); // has resulution of 0.01m + int lup = static_cast (ceil (min_dist*100)); // has resolution of 0.01m if (lup > 300) { // implicitly this caps the cost if there is a hole in the model lup = 300; @@ -520,7 +520,7 @@ costFunction4(float ref_val,float depth_val) { float disparity_diff = abs( ( -0.7253f/ref_val +1.0360f ) - depth_val ); - int top_lup = static_cast (ceil (disparity_diff*300)); // has resulution of 0.001m + int top_lup = static_cast (ceil (disparity_diff*300)); // has resolution of 0.001m if (top_lup > 300) { top_lup =300; @@ -529,7 +529,7 @@ costFunction4(float ref_val,float depth_val) // bottom: //bottom = bottom_lookup( round(mu*1000+1)); - int bottom_lup = static_cast (ceil( (depth_val) * 300)); // has resulution of 0.001m + int bottom_lup = static_cast (ceil( (depth_val) * 300)); // has resolution of 0.001m if (bottom_lup > 300) { bottom_lup =300; @@ -539,7 +539,7 @@ costFunction4(float ref_val,float depth_val) float proportion = 0.999f; float lhood = proportion + (1-proportion)*(top/bottom); - // safety fix thats seems to be required due to opengl ayschronizate + // safety fix that seems to be required due to opengl asynchronization // ask hordur about this if (bottom == 0) { @@ -650,7 +650,8 @@ pcl::simulation::RangeLikelihood::computeScores (float* reference, void pcl::simulation::RangeLikelihood::getPointCloud (pcl::PointCloud::Ptr pc, bool make_global, - const Eigen::Isometry3d & pose) + const Eigen::Isometry3d & pose, + bool organized) const { // TODO: check if this works for for rows/cols >1 and for width&height != 640x480 // i.e. multiple tiled images @@ -660,7 +661,7 @@ pcl::simulation::RangeLikelihood::getPointCloud (pcl::PointCloudwidth = camera_width_; //pc->height = camera_height_; - pc->is_dense = false; + pc->is_dense = true; pc->points.resize (pc->width*pc->height); int points_added = 0; @@ -680,8 +681,12 @@ pcl::simulation::RangeLikelihood::getPointCloud (pcl::PointCloud1 mapped disparity - int idx = points_added; // y*camera_width_ + x; + int idx; + if (organized) idx = y*col_width_ + x; + else idx = points_added; // y*camera_width_ + x; + float d = depth_buffer_[y*camera_width_ + x] ; + if (d < 1.0) // only add points with depth buffer less than max (20m) range { float z = zf*zn/((zf-zn)*(d - zf/(zf-zn))); @@ -696,17 +701,29 @@ pcl::simulation::RangeLikelihood::getPointCloud (pcl::PointCloudpoints[idx].x = (static_cast (x)-camera_cx_) * z * (-camera_fx_reciprocal_); pc->points[idx].y = (static_cast (y)-camera_cy_) * z * (-camera_fy_reciprocal_); - int rgb_idx = y*col_width_ + x; //camera_width_ + int rgb_idx = y*col_width_ + x; //camera_width_ pc->points[idx].b = color_buffer[rgb_idx*3+2]; // blue pc->points[idx].g = color_buffer[rgb_idx*3+1]; // green pc->points[idx].r = color_buffer[rgb_idx*3]; // red points_added++; } + else if (organized) + { + pc->is_dense = false; + pc->points[idx].z = std::numeric_limits::quiet_NaN (); + pc->points[idx].x = std::numeric_limits::quiet_NaN (); + pc->points[idx].y = std::numeric_limits::quiet_NaN (); + pc->points[idx].rgba = 0; + } } } - pc->width = 1; - pc->height = points_added; - pc->points.resize (points_added); + + if (!organized) + { + pc->width = 1; + pc->height = points_added; + pc->points.resize (points_added); + } if (make_global) { @@ -745,7 +762,7 @@ pcl::simulation::RangeLikelihood::getPointCloud (pcl::PointCloud~PCLVisualizer(); // viewer.reset(); cout << "done\n"; - // Problem: vtk and opengl dont seem to play very well together - // vtk seems to misbehave after a little while and wont keep the window on the screen + // Problem: vtk and opengl don't seem to play very well together + // vtk seems to misbehave after a little while and won't keep the window on the screen // method1: kill with [x] - but eventually it crashes: //while (!viewer.wasStopped ()){ diff --git a/simulation/tools/sim_viewer.cpp b/simulation/tools/sim_viewer.cpp index 70945395..12f4e901 100644 --- a/simulation/tools/sim_viewer.cpp +++ b/simulation/tools/sim_viewer.cpp @@ -392,7 +392,7 @@ boost::shared_ptr simpleVis (pcl::PointCloud< void capture (Eigen::Isometry3d pose_in, string point_cloud_fname) { - // No reference image - but this is kept for compatability with range_test_v2: + // No reference image - but this is kept for compatibility with range_test_v2: float* reference = new float[range_likelihood_->getRowHeight() * range_likelihood_->getColWidth()]; const float* depth_buffer = range_likelihood_->getDepthBuffer(); // Copy one image from our last as a reference. @@ -471,7 +471,7 @@ void capture (Eigen::Isometry3d pose_in, string point_cloud_fname) writer.writeBinary (point_cloud_fname, *pc_out); //cout << "finished writing file\n"; } - // Disabled all OpenCV stuff for now: dont want the dependency + // Disabled all OpenCV stuff for now: don't want the dependency /* bool demo_other_stuff = false; if (demo_other_stuff && write_cloud) diff --git a/simulation/tools/simulation_io.cpp b/simulation/tools/simulation_io.cpp index 1b6bfdb4..ca5b0707 100644 --- a/simulation/tools/simulation_io.cpp +++ b/simulation/tools/simulation_io.cpp @@ -97,7 +97,7 @@ pcl::simulation::SimExample::initializeGL (int argc, char** argv) void pcl::simulation::SimExample::doSim (Eigen::Isometry3d pose_in) { - // No reference image - but this is kept for compatability with range_test_v2: + // No reference image - but this is kept for compatibility with range_test_v2: float* reference = new float[rl_->getRowHeight() * rl_->getColWidth()]; const float* depth_buffer = rl_->getDepthBuffer(); // Copy one image from our last as a reference. diff --git a/stereo/include/pcl/stereo/stereo_matching.h b/stereo/include/pcl/stereo/stereo_matching.h index d3b7e969..0c6d3d0a 100644 --- a/stereo/include/pcl/stereo/stereo_matching.h +++ b/stereo/include/pcl/stereo/stereo_matching.h @@ -458,8 +458,8 @@ namespace pcl radius_ = radius; }; - /** \brief setter for the spatial bandwith used for cost aggregation based on adaptive weights - * \param[in] gamma_s spatial bandwith used for cost aggregation based on adaptive weights + /** \brief setter for the spatial bandwidth used for cost aggregation based on adaptive weights + * \param[in] gamma_s spatial bandwidth used for cost aggregation based on adaptive weights */ void setGammaS (int gamma_s) @@ -467,8 +467,8 @@ namespace pcl gamma_s_ = gamma_s; }; - /** \brief setter for the color bandwith used for cost aggregation based on adaptive weights - * \param[in] gamma_c color bandwith used for cost aggregation based on adaptive weights + /** \brief setter for the color bandwidth used for cost aggregation based on adaptive weights + * \param[in] gamma_c color bandwidth used for cost aggregation based on adaptive weights */ void setGammaC (int gamma_c) diff --git a/surface/include/pcl/surface/3rdparty/opennurbs/opennurbs_beam.h b/surface/include/pcl/surface/3rdparty/opennurbs/opennurbs_beam.h index 88707a79..f5111d1e 100644 --- a/surface/include/pcl/surface/3rdparty/opennurbs/opennurbs_beam.h +++ b/surface/include/pcl/surface/3rdparty/opennurbs/opennurbs_beam.h @@ -7,7 +7,7 @@ Description: 2d xy profile to 3d world space. Parameters: P - [in] start or end of path - T - [in] unit tanget to path + T - [in] unit tangent to path U - [in] unit up vector perpendicular to T Normal - [in] optional unit vector with Normal->z > 0 that defines the unit normal to the miter plane. diff --git a/surface/include/pcl/surface/3rdparty/opennurbs/opennurbs_bezier.h b/surface/include/pcl/surface/3rdparty/opennurbs/opennurbs_bezier.h index 29bdae93..b55636b2 100644 --- a/surface/include/pcl/surface/3rdparty/opennurbs/opennurbs_bezier.h +++ b/surface/include/pcl/surface/3rdparty/opennurbs/opennurbs_bezier.h @@ -1416,7 +1416,7 @@ public: order1 - [in] order2 - [in] Returns: - True if input was valid and creation succeded. + True if input was valid and creation succeeded. */ bool Create( int dim, diff --git a/surface/include/pcl/surface/3rdparty/opennurbs/opennurbs_polyline.h b/surface/include/pcl/surface/3rdparty/opennurbs/opennurbs_polyline.h index e0437729..a4cffe61 100644 --- a/surface/include/pcl/surface/3rdparty/opennurbs/opennurbs_polyline.h +++ b/surface/include/pcl/surface/3rdparty/opennurbs/opennurbs_polyline.h @@ -41,7 +41,7 @@ public: // Description: // Create a regular polygon circumscribe about a circle. - // The midpoints of the polygon's edges will be tanget to the + // The midpoints of the polygon's edges will be tangent to the // circle. // Parameters: // circle - [in] diff --git a/surface/include/pcl/surface/3rdparty/poisson4/bspline_data.hpp b/surface/include/pcl/surface/3rdparty/poisson4/bspline_data.hpp index 6e688c54..bcc0bb48 100644 --- a/surface/include/pcl/surface/3rdparty/poisson4/bspline_data.hpp +++ b/surface/include/pcl/surface/3rdparty/poisson4/bspline_data.hpp @@ -84,6 +84,8 @@ namespace pcl { vvDotTable = dvDotTable = ddDotTable = NULL; valueTables = dValueTables = NULL; + baseFunctions = NULL; + baseBSplines = NULL; functionCount = sampleCount = 0; } @@ -98,9 +100,14 @@ namespace pcl if( valueTables ) delete[] valueTables; if( dValueTables ) delete[] dValueTables; + + if( baseFunctions ) delete[] baseFunctions; + if( baseBSplines ) delete[] baseBSplines; } vvDotTable = dvDotTable = ddDotTable = NULL; valueTables = dValueTables=NULL; + baseFunctions = NULL; + baseBSplines = NULL; functionCount = 0; } diff --git a/surface/include/pcl/surface/bilateral_upsampling.h b/surface/include/pcl/surface/bilateral_upsampling.h index 555b2aef..98e4e0bd 100644 --- a/surface/include/pcl/surface/bilateral_upsampling.h +++ b/surface/include/pcl/surface/bilateral_upsampling.h @@ -48,7 +48,7 @@ namespace pcl /** \brief Bilateral filtering implementation, based on the following paper: * * Kopf, Johannes and Cohen, Michael F. and Lischinski, Dani and Uyttendaele, Matt - Joint Bilateral Upsampling, - * * ACM Transations in Graphics, July 2007 + * * ACM Transactions in Graphics, July 2007 * * Takes in a colored organized point cloud (i.e. PointXYZRGB or PointXYZRGBA), that might contain nan values for the * depth information, and it will return an upsampled version of this cloud, based on the formula: diff --git a/surface/include/pcl/surface/concave_hull.h b/surface/include/pcl/surface/concave_hull.h index ef20939f..85b296b0 100644 --- a/surface/include/pcl/surface/concave_hull.h +++ b/surface/include/pcl/surface/concave_hull.h @@ -132,11 +132,6 @@ namespace pcl { keep_information_ = value; } - - /** \brief Returns the dimensionality (2 or 3) of the calculated hull. */ - PCL_DEPRECATED ("[pcl::ConcaveHull::getDim] This method is deprecated. Please use getDimension () instead.") - int - getDim () const; /** \brief Returns the dimensionality (2 or 3) of the calculated hull. */ inline int diff --git a/surface/include/pcl/surface/gp3.h b/surface/include/pcl/surface/gp3.h index 50218ee1..192254aa 100644 --- a/surface/include/pcl/surface/gp3.h +++ b/surface/include/pcl/surface/gp3.h @@ -60,8 +60,8 @@ namespace pcl * when taking into account the segment between the points S1 and S2 * \param X 2D coordinate of the point * \param S1 2D coordinate of the segment's first point - * \param S2 2D coordinate of the segment's secont point - * \param R 2D coorddinate of the reference point (defaults to 0,0) + * \param S2 2D coordinate of the segment's second point + * \param R 2D coordinate of the reference point (defaults to 0,0) * \ingroup surface */ inline bool @@ -416,7 +416,7 @@ namespace pcl /** \brief 2D coordinates of the second fringe neighbor of the next point **/ Eigen::Vector2f uvn_next_sfn_; - /** \brief Temporary variable to store 3 coordiantes **/ + /** \brief Temporary variable to store 3 coordinates **/ Eigen::Vector3f tmp_; /** \brief The actual surface reconstruction method. diff --git a/surface/include/pcl/surface/grid_projection.h b/surface/include/pcl/surface/grid_projection.h index ce5dd832..48807e9d 100644 --- a/surface/include/pcl/surface/grid_projection.h +++ b/surface/include/pcl/surface/grid_projection.h @@ -125,7 +125,7 @@ namespace pcl * points within the padding area,and do a weighted average. Say if the padding * size is 1, when we process cell (x,y,z), we will find union of input data points * from (x-1) to (x+1), (y-1) to (y+1), (z-1) to (z+1)(in total, 27 cells). In this - * way, even the cells itself doesnt contain any data points, we will stil process it + * way, even the cells itself doesn't contain any data points, we will still process it * because there are data points in the padding area. This can help us fix holes which * is smaller than the padding size. * \param padding_size The num of padding cells we want to create diff --git a/surface/include/pcl/surface/impl/bilateral_upsampling.hpp b/surface/include/pcl/surface/impl/bilateral_upsampling.hpp index 98298f1c..a6bfde00 100644 --- a/surface/include/pcl/surface/impl/bilateral_upsampling.hpp +++ b/surface/include/pcl/surface/impl/bilateral_upsampling.hpp @@ -106,17 +106,15 @@ pcl::BilateralUpsampling::performProcessing (PointCloudOut for (int x_w = start_window_x; x_w < end_window_x; ++ x_w) for (int y_w = start_window_y; y_w < end_window_y; ++ y_w) { - float dx = float (x - x_w), - dy = float (y - y_w); - - float val_exp_depth = val_exp_depth_matrix(dx+window_size_, dy+window_size_); - - float d_color = static_cast ( + float val_exp_depth = val_exp_depth_matrix (static_cast (x - x_w + window_size_), + static_cast (y - y_w + window_size_)); + + Eigen::VectorXf::Index d_color = static_cast ( abs (input_->points[y_w * input_->width + x_w].r - input_->points[y * input_->width + x].r) + abs (input_->points[y_w * input_->width + x_w].g - input_->points[y * input_->width + x].g) + abs (input_->points[y_w * input_->width + x_w].b - input_->points[y * input_->width + x].b)); - float val_exp_rgb = val_exp_rgb_vector (static_cast (d_color)); + float val_exp_rgb = val_exp_rgb_vector (d_color); if (pcl_isfinite (input_->points[y_w*input_->width + x_w].z)) { diff --git a/surface/include/pcl/surface/impl/concave_hull.hpp b/surface/include/pcl/surface/impl/concave_hull.hpp index dae1be74..9b220818 100644 --- a/surface/include/pcl/surface/impl/concave_hull.hpp +++ b/surface/include/pcl/surface/impl/concave_hull.hpp @@ -55,16 +55,6 @@ #include #include -////////////////////////////////////////////////////////////////////////// -/** \brief Get dimension of concave hull - * \return dimension - */ -template int -pcl::ConcaveHull::getDim () const -{ - return (getDimension ()); -} - ////////////////////////////////////////////////////////////////////////// template void pcl::ConcaveHull::reconstruct (PointCloud &output) @@ -131,7 +121,7 @@ pcl::ConcaveHull::performReconstruction (PointCloud &alpha_shape, std: { Eigen::Vector4d xyz_centroid; compute3DCentroid (*input_, *indices_, xyz_centroid); - EIGEN_ALIGN16 Eigen::Matrix3d covariance_matrix; + EIGEN_ALIGN16 Eigen::Matrix3d covariance_matrix = Eigen::Matrix3d::Zero (); computeCovarianceMatrixNormalized (*input_, *indices_, xyz_centroid, covariance_matrix); // Check if the covariance matrix is finite or not. @@ -252,7 +242,7 @@ pcl::ConcaveHull::performReconstruction (PointCloud &alpha_shape, std: int max_vertex_id = 0; FORALLvertices { - if (vertex->id + 1 > max_vertex_id) + if (vertex->id + 1 > unsigned (max_vertex_id)) max_vertex_id = vertex->id + 1; } diff --git a/surface/include/pcl/surface/impl/convex_hull.hpp b/surface/include/pcl/surface/impl/convex_hull.hpp index 0af1edca..fb0af36f 100644 --- a/surface/include/pcl/surface/impl/convex_hull.hpp +++ b/surface/include/pcl/surface/impl/convex_hull.hpp @@ -59,7 +59,7 @@ pcl::ConvexHull::calculateInputDimension () PCL_DEBUG ("[pcl::%s::calculateInputDimension] WARNING: Input dimension not specified. Automatically determining input dimension.\n", getClassName ().c_str ()); Eigen::Vector4d xyz_centroid; compute3DCentroid (*input_, *indices_, xyz_centroid); - EIGEN_ALIGN16 Eigen::Matrix3d covariance_matrix; + EIGEN_ALIGN16 Eigen::Matrix3d covariance_matrix = Eigen::Matrix3d::Zero (); computeCovarianceMatrixNormalized (*input_, *indices_, xyz_centroid, covariance_matrix); EIGEN_ALIGN16 Eigen::Vector3d eigen_values; diff --git a/surface/include/pcl/surface/impl/marching_cubes.hpp b/surface/include/pcl/surface/impl/marching_cubes.hpp index 16b7690f..9c055a40 100644 --- a/surface/include/pcl/surface/impl/marching_cubes.hpp +++ b/surface/include/pcl/surface/impl/marching_cubes.hpp @@ -42,13 +42,6 @@ #include #include -////////////////////////////////////////////////////////////////////////////////////////////// -template -pcl::MarchingCubes::MarchingCubes () -: min_p_ (), max_p_ (), percentage_extend_grid_ (), iso_level_ () -{ -} - ////////////////////////////////////////////////////////////////////////////////////////////// template pcl::MarchingCubes::~MarchingCubes () @@ -59,26 +52,17 @@ pcl::MarchingCubes::~MarchingCubes () template void pcl::MarchingCubes::getBoundingBox () { - pcl::getMinMax3D (*input_, min_p_, max_p_); - - min_p_ -= (max_p_ - min_p_) * percentage_extend_grid_/2; - max_p_ += (max_p_ - min_p_) * percentage_extend_grid_/2; - - Eigen::Vector4f bounding_box_size = max_p_ - min_p_; - - bounding_box_size = max_p_ - min_p_; - PCL_DEBUG ("[pcl::MarchingCubesHoppe::getBoundingBox] Size of Bounding Box is [%f, %f, %f]\n", - bounding_box_size.x (), bounding_box_size.y (), bounding_box_size.z ()); - double max_size = - (std::max) ((std::max)(bounding_box_size.x (), bounding_box_size.y ()), - bounding_box_size.z ()); - (void)max_size; - // ???? - // data_size_ = static_cast (max_size / leaf_size_); - PCL_DEBUG ("[pcl::MarchingCubesHoppe::getBoundingBox] Lower left point is [%f, %f, %f]\n", - min_p_.x (), min_p_.y (), min_p_.z ()); - PCL_DEBUG ("[pcl::MarchingCubesHoppe::getBoundingBox] Upper left point is [%f, %f, %f]\n", - max_p_.x (), max_p_.y (), max_p_.z ()); + PointNT max_pt, min_pt; + pcl::getMinMax3D (*input_, min_pt, max_pt); + + lower_boundary_ = min_pt.getArray3fMap (); + upper_boundary_ = max_pt.getArray3fMap (); + + const Eigen::Array3f size3_extend = 0.5f * percentage_extend_grid_ + * (upper_boundary_ - lower_boundary_); + + lower_boundary_ -= size3_extend; + upper_boundary_ += size3_extend; } @@ -90,19 +74,18 @@ pcl::MarchingCubes::interpolateEdge (Eigen::Vector3f &p1, float val_p2, Eigen::Vector3f &output) { - float mu = (iso_level_ - val_p1) / (val_p2-val_p1); + const float mu = (iso_level_ - val_p1) / (val_p2 - val_p1); output = p1 + mu * (p2 - p1); } ////////////////////////////////////////////////////////////////////////////////////////////// template void -pcl::MarchingCubes::createSurface (std::vector &leaf_node, - Eigen::Vector3i &index_3d, +pcl::MarchingCubes::createSurface (const std::vector &leaf_node, + const Eigen::Vector3i &index_3d, pcl::PointCloud &cloud) { int cubeindex = 0; - Eigen::Vector3f vertex_list[12]; if (leaf_node[0] < iso_level_) cubeindex |= 1; if (leaf_node[1] < iso_level_) cubeindex |= 2; if (leaf_node[2] < iso_level_) cubeindex |= 4; @@ -116,31 +99,29 @@ pcl::MarchingCubes::createSurface (std::vector &leaf_node, if (edgeTable[cubeindex] == 0) return; - //Eigen::Vector4f index_3df (index_3d[0], index_3d[1], index_3d[2], 0.0f); - Eigen::Vector3f center;// TODO coeff wise product = min_p_ + Eigen::Vector4f (1.0f/res_x_, 1.0f/res_y_, 1.0f/res_z_) * index_3df * (max_p_ - min_p_); - center[0] = min_p_[0] + (max_p_[0] - min_p_[0]) * float (index_3d[0]) / float (res_x_); - center[1] = min_p_[1] + (max_p_[1] - min_p_[1]) * float (index_3d[1]) / float (res_y_); - center[2] = min_p_[2] + (max_p_[2] - min_p_[2]) * float (index_3d[2]) / float (res_z_); + const Eigen::Vector3f center = lower_boundary_ + + size_voxel_ * index_3d.cast ().array (); std::vector > p; p.resize (8); for (int i = 0; i < 8; ++i) { Eigen::Vector3f point = center; - if(i & 0x4) - point[1] = static_cast (center[1] + (max_p_[1] - min_p_[1]) / float (res_y_)); + if (i & 0x4) + point[1] = static_cast (center[1] + size_voxel_[1]); - if(i & 0x2) - point[2] = static_cast (center[2] + (max_p_[2] - min_p_[2]) / float (res_z_)); + if (i & 0x2) + point[2] = static_cast (center[2] + size_voxel_[2]); - if((i & 0x1) ^ ((i >> 1) & 0x1)) - point[0] = static_cast (center[0] + (max_p_[0] - min_p_[0]) / float (res_x_)); + if ((i & 0x1) ^ ((i >> 1) & 0x1)) + point[0] = static_cast (center[0] + size_voxel_[0]); p[i] = point; } - // Find the vertices where the surface intersects the cube + std::vector > vertex_list; + vertex_list.resize (12); if (edgeTable[cubeindex] & 1) interpolateEdge (p[0], p[1], leaf_node[0], leaf_node[1], vertex_list[0]); if (edgeTable[cubeindex] & 2) @@ -167,20 +148,14 @@ pcl::MarchingCubes::createSurface (std::vector &leaf_node, interpolateEdge (p[3], p[7], leaf_node[3], leaf_node[7], vertex_list[11]); // Create the triangle - for (int i = 0; triTable[cubeindex][i] != -1; i+=3) + for (int i = 0; triTable[cubeindex][i] != -1; i += 3) { - PointNT p1,p2,p3; - p1.x = vertex_list[triTable[cubeindex][i ]][0]; - p1.y = vertex_list[triTable[cubeindex][i ]][1]; - p1.z = vertex_list[triTable[cubeindex][i ]][2]; + PointNT p1, p2, p3; + p1.getVector3fMap () = vertex_list[triTable[cubeindex][i]]; cloud.push_back (p1); - p2.x = vertex_list[triTable[cubeindex][i+1]][0]; - p2.y = vertex_list[triTable[cubeindex][i+1]][1]; - p2.z = vertex_list[triTable[cubeindex][i+1]][2]; + p2.getVector3fMap () = vertex_list[triTable[cubeindex][i+1]]; cloud.push_back (p2); - p3.x = vertex_list[triTable[cubeindex][i+2]][0]; - p3.y = vertex_list[triTable[cubeindex][i+2]][1]; - p3.z = vertex_list[triTable[cubeindex][i+2]][2]; + p3.getVector3fMap () = vertex_list[triTable[cubeindex][i+2]]; cloud.push_back (p3); } } @@ -191,7 +166,7 @@ template void pcl::MarchingCubes::getNeighborList1D (std::vector &leaf, Eigen::Vector3i &index3d) { - leaf = std::vector (8, 0.0f); + leaf.resize (8); leaf[0] = getGridValue (index3d); leaf[1] = getGridValue (index3d + Eigen::Vector3i (1, 0, 0)); @@ -201,6 +176,15 @@ pcl::MarchingCubes::getNeighborList1D (std::vector &leaf, leaf[5] = getGridValue (index3d + Eigen::Vector3i (1, 1, 0)); leaf[6] = getGridValue (index3d + Eigen::Vector3i (1, 1, 1)); leaf[7] = getGridValue (index3d + Eigen::Vector3i (0, 1, 1)); + + for (int i = 0; i < 8; ++i) + { + if (std::isnan (leaf[i])) + { + leaf.clear (); + break; + } + } } @@ -224,53 +208,11 @@ pcl::MarchingCubes::getGridValue (Eigen::Vector3i pos) template void pcl::MarchingCubes::performReconstruction (pcl::PolygonMesh &output) { - if (!(iso_level_ >= 0 && iso_level_ < 1)) - { - PCL_ERROR ("[pcl::%s::performReconstruction] Invalid iso level %f! Please use a number between 0 and 1.\n", getClassName ().c_str (), iso_level_); - output.cloud.width = output.cloud.height = 0; - output.cloud.data.clear (); - output.polygons.clear (); - return; - } - - // Create grid - grid_ = std::vector (res_x_*res_y_*res_z_, 0.0f); - - // Populate tree - tree_->setInputCloud (input_); - - getBoundingBox (); - - // Transform the point cloud into a voxel grid - // This needs to be implemented in a child class - voxelizeData (); - + pcl::PointCloud points; + performReconstruction (points, output.polygons); - // Run the actual marching cubes algorithm, store it into a point cloud, - // and copy the point cloud + connectivity into output - pcl::PointCloud cloud; - - for (int x = 1; x < res_x_-1; ++x) - for (int y = 1; y < res_y_-1; ++y) - for (int z = 1; z < res_z_-1; ++z) - { - Eigen::Vector3i index_3d (x, y, z); - std::vector leaf_node; - getNeighborList1D (leaf_node, index_3d); - createSurface (leaf_node, index_3d, cloud); - } - pcl::toPCLPointCloud2 (cloud, output.cloud); - - output.polygons.resize (cloud.size () / 3); - for (size_t i = 0; i < output.polygons.size (); ++i) - { - pcl::Vertices v; - v.vertices.resize (3); - for (int j = 0; j < 3; ++j) - v.vertices[j] = static_cast (i) * 3 + j; - output.polygons[i] = v; - } + pcl::toPCLPointCloud2 (points, output.cloud); } @@ -281,28 +223,37 @@ pcl::MarchingCubes::performReconstruction (pcl::PointCloud &po { if (!(iso_level_ >= 0 && iso_level_ < 1)) { - PCL_ERROR ("[pcl::%s::performReconstruction] Invalid iso level %f! Please use a number between 0 and 1.\n", getClassName ().c_str (), iso_level_); + PCL_ERROR ("[pcl::%s::performReconstruction] Invalid iso level %f! Please use a number between 0 and 1.\n", + getClassName ().c_str (), iso_level_); points.width = points.height = 0; points.points.clear (); polygons.clear (); return; } + // the point cloud really generated from Marching Cubes, prev intermediate_cloud_ + pcl::PointCloud intermediate_cloud; + // Create grid - grid_ = std::vector (res_x_*res_y_*res_z_, 0.0f); + grid_ = std::vector (res_x_*res_y_*res_z_, NAN); // Populate tree tree_->setInputCloud (input_); + // Compute bounding box and voxel size getBoundingBox (); + size_voxel_ = (upper_boundary_ - lower_boundary_) + * Eigen::Array3f (res_x_, res_y_, res_z_).inverse (); // Transform the point cloud into a voxel grid // This needs to be implemented in a child class voxelizeData (); - // Run the actual marching cubes algorithm, store it into a point cloud, - // and copy the point cloud + connectivity into output - points.clear (); + // preallocate memory assuming a hull. suppose 6 point per voxel + double size_reserve = std::min((double) intermediate_cloud.points.max_size (), + 2.0 * 6.0 * (double) (res_y_*res_z_ + res_x_*res_z_ + res_x_*res_y_)); + intermediate_cloud.reserve ((size_t) size_reserve); + for (int x = 1; x < res_x_-1; ++x) for (int y = 1; y < res_y_-1; ++y) for (int z = 1; z < res_z_-1; ++z) @@ -310,9 +261,12 @@ pcl::MarchingCubes::performReconstruction (pcl::PointCloud &po Eigen::Vector3i index_3d (x, y, z); std::vector leaf_node; getNeighborList1D (leaf_node, index_3d); - createSurface (leaf_node, index_3d, points); + if (!leaf_node.empty ()) + createSurface (leaf_node, index_3d, intermediate_cloud); } + points.swap (intermediate_cloud); + polygons.resize (points.size () / 3); for (size_t i = 0; i < polygons.size (); ++i) { @@ -324,8 +278,6 @@ pcl::MarchingCubes::performReconstruction (pcl::PointCloud &po } } - - #define PCL_INSTANTIATE_MarchingCubes(T) template class PCL_EXPORTS pcl::MarchingCubes; #endif // PCL_SURFACE_IMPL_MARCHING_CUBES_H_ diff --git a/surface/include/pcl/surface/impl/marching_cubes_hoppe.hpp b/surface/include/pcl/surface/impl/marching_cubes_hoppe.hpp index 7e4b4b51..4e521fe8 100644 --- a/surface/include/pcl/surface/impl/marching_cubes_hoppe.hpp +++ b/surface/include/pcl/surface/impl/marching_cubes_hoppe.hpp @@ -42,13 +42,6 @@ #include #include -////////////////////////////////////////////////////////////////////////////////////////////// -template -pcl::MarchingCubesHoppe::MarchingCubesHoppe () - : MarchingCubes () -{ -} - ////////////////////////////////////////////////////////////////////////////////////////////// template pcl::MarchingCubesHoppe::~MarchingCubesHoppe () @@ -60,26 +53,38 @@ pcl::MarchingCubesHoppe::~MarchingCubesHoppe () template void pcl::MarchingCubesHoppe::voxelizeData () { + const bool is_far_ignored = dist_ignore_ > 0.0f; + for (int x = 0; x < res_x_; ++x) + { + const int y_start = x * res_y_ * res_z_; + for (int y = 0; y < res_y_; ++y) + { + const int z_start = y_start + y * res_z_; + for (int z = 0; z < res_z_; ++z) { - std::vector nn_indices; - std::vector nn_sqr_dists; - - Eigen::Vector3f point; - point[0] = min_p_[0] + (max_p_[0] - min_p_[0]) * float (x) / float (res_x_); - point[1] = min_p_[1] + (max_p_[1] - min_p_[1]) * float (y) / float (res_y_); - point[2] = min_p_[2] + (max_p_[2] - min_p_[2]) * float (z) / float (res_z_); - + std::vector nn_indices (1, 0); + std::vector nn_sqr_dists (1, 0.0f); + const Eigen::Vector3f point = (lower_boundary_ + size_voxel_ * Eigen::Array3f (x, y, z)).matrix (); PointNT p; + p.getVector3fMap () = point; tree_->nearestKSearch (p, 1, nn_indices, nn_sqr_dists); - grid_[x * res_y_*res_z_ + y * res_z_ + z] = input_->points[nn_indices[0]].getNormalVector3fMap ().dot ( - point - input_->points[nn_indices[0]].getVector3fMap ()); + if (!is_far_ignored || nn_sqr_dists[0] < dist_ignore_) + { + const Eigen::Vector3f normal = input_->points[nn_indices[0]].getNormalVector3fMap (); + + if (!std::isnan (normal (0)) && normal.norm () > 0.5f) + grid_[z_start + z] = normal.dot ( + point - input_->points[nn_indices[0]].getVector3fMap ()); + } } + } + } } diff --git a/surface/include/pcl/surface/impl/marching_cubes_rbf.hpp b/surface/include/pcl/surface/impl/marching_cubes_rbf.hpp index 33552f37..8e1e5e59 100644 --- a/surface/include/pcl/surface/impl/marching_cubes_rbf.hpp +++ b/surface/include/pcl/surface/impl/marching_cubes_rbf.hpp @@ -45,14 +45,6 @@ #include #include -////////////////////////////////////////////////////////////////////////////////////////////// -template -pcl::MarchingCubesRBF::MarchingCubesRBF () - : MarchingCubes (), - off_surface_epsilon_ (0.1f) -{ -} - ////////////////////////////////////////////////////////////////////////////////////////////// template pcl::MarchingCubesRBF::~MarchingCubesRBF () @@ -64,7 +56,7 @@ template void pcl::MarchingCubesRBF::voxelizeData () { // Initialize data structures - unsigned int N = static_cast (input_->size ()); + const unsigned int N = static_cast (input_->size ()); Eigen::MatrixXd M (2*N, 2*N), d (2*N, 1); @@ -103,10 +95,9 @@ pcl::MarchingCubesRBF::voxelizeData () for (int y = 0; y < res_y_; ++y) for (int z = 0; z < res_z_; ++z) { - Eigen::Vector3d point; - point[0] = min_p_[0] + (max_p_[0] - min_p_[0]) * float (x) / float (res_x_); - point[1] = min_p_[1] + (max_p_[1] - min_p_[1]) * float (y) / float (res_y_); - point[2] = min_p_[2] + (max_p_[2] - min_p_[2]) * float (z) / float (res_z_); + const Eigen::Vector3f point_f = (size_voxel_ * Eigen::Array3f (x, y, z) + + lower_boundary_).matrix (); + const Eigen::Vector3d point = point_f.cast (); double f = 0.0; std::vector::const_iterator w_it (weights.begin()); diff --git a/surface/include/pcl/surface/impl/mls.hpp b/surface/include/pcl/surface/impl/mls.hpp index f7df1399..ceaa76dd 100644 --- a/surface/include/pcl/surface/impl/mls.hpp +++ b/surface/include/pcl/surface/impl/mls.hpp @@ -47,6 +47,7 @@ #include #include #include +#include #ifdef _OPENMP #include @@ -70,7 +71,6 @@ pcl::MovingLeastSquares::process (PointCloudOut &output) normals_->points.clear (); } - // Copy the header output.header = input_->header; output.width = output.height = 0; @@ -92,7 +92,6 @@ pcl::MovingLeastSquares::process (PointCloudOut &output) if (!initCompute ()) return; - // Initialize the spatial locator if (!tree_) { @@ -117,21 +116,28 @@ pcl::MovingLeastSquares::process (PointCloudOut &output) boost::uniform_real uniform_distrib (-tmp, tmp); rng_uniform_distribution_.reset (new boost::variate_generator > (rng_alg_, uniform_distrib)); - mls_results_.resize (1); // Need to have a reference to a single dummy result. - break; } case (VOXEL_GRID_DILATION): case (DISTINCT_CLOUD): - { - mls_results_.resize (input_->size ()); - break; - } + { + if (!cache_mls_results_) + PCL_WARN ("The cache mls results is forced when using upsampling method VOXEL_GRID_DILATION or DISTINCT_CLOUD.\n"); + + cache_mls_results_ = true; + break; + } default: - { - mls_results_.resize (1); // Need to have a reference to a single dummy result. - break; - } + break; + } + + if (cache_mls_results_) + { + mls_results_.resize (input_->size ()); + } + else + { + mls_results_.resize (1); // Need to have a reference to a single dummy result. } // Perform the actual surface reconstruction @@ -164,7 +170,6 @@ pcl::MovingLeastSquares::process (PointCloudOut &output) template void pcl::MovingLeastSquares::computeMLSPointNormal (int index, const std::vector &nn_indices, - std::vector &nn_sqr_dists, PointCloudOut &projected_points, NormalCloud &projected_points_normals, PointIndices &corresponding_input_indices, @@ -173,136 +178,14 @@ pcl::MovingLeastSquares::computeMLSPointNormal (int index, // Note: this method is const because it needs to be thread-safe // (MovingLeastSquaresOMP calls it from multiple threads) - // Compute the plane coefficients - EIGEN_ALIGN16 Eigen::Matrix3d covariance_matrix; - Eigen::Vector4d xyz_centroid; - - // Estimate the XYZ centroid - pcl::compute3DCentroid (*input_, nn_indices, xyz_centroid); - - // Compute the 3x3 covariance matrix - pcl::computeCovarianceMatrix (*input_, nn_indices, xyz_centroid, covariance_matrix); - EIGEN_ALIGN16 Eigen::Vector3d::Scalar eigen_value; - EIGEN_ALIGN16 Eigen::Vector3d eigen_vector; - Eigen::Vector4d model_coefficients; - pcl::eigen33 (covariance_matrix, eigen_value, eigen_vector); - model_coefficients.head<3> ().matrix () = eigen_vector; - model_coefficients[3] = 0; - model_coefficients[3] = -1 * model_coefficients.dot (xyz_centroid); - - // Projected query point - Eigen::Vector3d point = input_->points[index].getVector3fMap ().template cast (); - double distance = point.dot (model_coefficients.head<3> ()) + model_coefficients[3]; - point -= distance * model_coefficients.head<3> (); - - float curvature = static_cast (covariance_matrix.trace ()); - // Compute the curvature surface change - if (curvature != 0) - curvature = fabsf (float (eigen_value / double (curvature))); - - - // Get a copy of the plane normal easy access - Eigen::Vector3d plane_normal = model_coefficients.head<3> (); - // Vector in which the polynomial coefficients will be put - Eigen::VectorXd c_vec; - // Local coordinate system (Darboux frame) - Eigen::Vector3d v_axis (0.0f, 0.0f, 0.0f), u_axis (0.0f, 0.0f, 0.0f); - - - - // Perform polynomial fit to update point and normal - //////////////////////////////////////////////////// - if (polynomial_fit_ && static_cast (nn_indices.size ()) >= nr_coeff_) - { - // Update neighborhood, since point was projected, and computing relative - // positions. Note updating only distances for the weights for speed - std::vector > de_meaned (nn_indices.size ()); - for (size_t ni = 0; ni < nn_indices.size (); ++ni) - { - de_meaned[ni][0] = input_->points[nn_indices[ni]].x - point[0]; - de_meaned[ni][1] = input_->points[nn_indices[ni]].y - point[1]; - de_meaned[ni][2] = input_->points[nn_indices[ni]].z - point[2]; - nn_sqr_dists[ni] = static_cast (de_meaned[ni].dot (de_meaned[ni])); - } - - // Allocate matrices and vectors to hold the data used for the polynomial fit - Eigen::VectorXd weight_vec (nn_indices.size ()); - Eigen::MatrixXd P (nr_coeff_, nn_indices.size ()); - Eigen::VectorXd f_vec (nn_indices.size ()); - Eigen::MatrixXd P_weight; // size will be (nr_coeff_, nn_indices.size ()); - Eigen::MatrixXd P_weight_Pt (nr_coeff_, nr_coeff_); - - // Get local coordinate system (Darboux frame) - v_axis = plane_normal.unitOrthogonal (); - u_axis = plane_normal.cross (v_axis); - - // Go through neighbors, transform them in the local coordinate system, - // save height and the evaluation of the polynome's terms - double u_coord, v_coord, u_pow, v_pow; - for (size_t ni = 0; ni < nn_indices.size (); ++ni) - { - // (Re-)compute weights - weight_vec (ni) = exp (-nn_sqr_dists[ni] / sqr_gauss_param_); - - // Transforming coordinates - u_coord = de_meaned[ni].dot (u_axis); - v_coord = de_meaned[ni].dot (v_axis); - f_vec (ni) = de_meaned[ni].dot (plane_normal); - - // Compute the polynomial's terms at the current point - int j = 0; - u_pow = 1; - for (int ui = 0; ui <= order_; ++ui) - { - v_pow = 1; - for (int vi = 0; vi <= order_ - ui; ++vi) - { - P (j++, ni) = u_pow * v_pow; - v_pow *= v_coord; - } - u_pow *= u_coord; - } - } - - // Computing coefficients - P_weight = P * weight_vec.asDiagonal (); - P_weight_Pt = P_weight * P.transpose (); - c_vec = P_weight * f_vec; - P_weight_Pt.llt ().solveInPlace (c_vec); - } + mls_result.computeMLSSurface (*input_, index, nn_indices, search_radius_, order_); switch (upsample_method_) { case (NONE): { - Eigen::Vector3d normal = plane_normal; - - if (polynomial_fit_ && static_cast (nn_indices.size ()) >= nr_coeff_ && pcl_isfinite (c_vec[0])) - { - point += (c_vec[0] * plane_normal); - - // Compute tangent vectors using the partial derivates evaluated at (0,0) which is c_vec[order_+1] and c_vec[1] - if (compute_normals_) - normal = plane_normal - c_vec[order_ + 1] * u_axis - c_vec[1] * v_axis; - } - - PointOutT aux; - aux.x = static_cast (point[0]); - aux.y = static_cast (point[1]); - aux.z = static_cast (point[2]); - projected_points.push_back (aux); - - if (compute_normals_) - { - pcl::Normal aux_normal; - aux_normal.normal_x = static_cast (normal[0]); - aux_normal.normal_y = static_cast (normal[1]); - aux_normal.normal_z = static_cast (normal[2]); - aux_normal.curvature = curvature; - projected_points_normals.push_back (aux_normal); - corresponding_input_indices.indices.push_back (index); - } - + MLSResult::MLSProjectionResults proj = mls_result.projectQueryPoint (projection_method_, nr_coeff_); + addProjectedPointNormal (index, proj.point, proj.normal, mls_result.curvature, projected_points, projected_points_normals, corresponding_input_indices); break; } @@ -311,19 +194,10 @@ pcl::MovingLeastSquares::computeMLSPointNormal (int index, // Uniformly sample a circle around the query point using the radius and step parameters for (float u_disp = -static_cast (upsampling_radius_); u_disp <= upsampling_radius_; u_disp += static_cast (upsampling_step_)) for (float v_disp = -static_cast (upsampling_radius_); v_disp <= upsampling_radius_; v_disp += static_cast (upsampling_step_)) - if (u_disp*u_disp + v_disp*v_disp < upsampling_radius_*upsampling_radius_) + if (u_disp * u_disp + v_disp * v_disp < upsampling_radius_ * upsampling_radius_) { - PointOutT projected_point; - pcl::Normal projected_normal; - projectPointToMLSSurface (u_disp, v_disp, u_axis, v_axis, plane_normal, point, - curvature, c_vec, - static_cast (nn_indices.size ()), - projected_point, projected_normal); - - projected_points.push_back (projected_point); - corresponding_input_indices.indices.push_back (index); - if (compute_normals_) - projected_points_normals.push_back (projected_normal); + MLSResult::MLSProjectionResults proj = mls_result.projectPointSimpleToPolynomialSurface (u_disp, v_disp); + addProjectedPointNormal (index, proj.point, proj.normal, mls_result.curvature, projected_points, projected_points_normals, corresponding_input_indices); } break; } @@ -337,128 +211,69 @@ pcl::MovingLeastSquares::computeMLSPointNormal (int index, if (num_points_to_add <= 0) { // Just add the current point - Eigen::Vector3d normal = plane_normal; - if (polynomial_fit_ && static_cast (nn_indices.size ()) >= nr_coeff_ && pcl_isfinite (c_vec[0])) - { - // Projection onto MLS surface along Darboux normal to the height at (0,0) - point += (c_vec[0] * plane_normal); - // Compute tangent vectors using the partial derivates evaluated at (0,0) which is c_vec[order_+1] and c_vec[1] - if (compute_normals_) - normal = plane_normal - c_vec[order_ + 1] * u_axis - c_vec[1] * v_axis; - } - PointOutT aux; - aux.x = static_cast (point[0]); - aux.y = static_cast (point[1]); - aux.z = static_cast (point[2]); - projected_points.push_back (aux); - corresponding_input_indices.indices.push_back (index); - - if (compute_normals_) - { - pcl::Normal aux_normal; - aux_normal.normal_x = static_cast (normal[0]); - aux_normal.normal_y = static_cast (normal[1]); - aux_normal.normal_z = static_cast (normal[2]); - aux_normal.curvature = curvature; - projected_points_normals.push_back (aux_normal); - } + MLSResult::MLSProjectionResults proj = mls_result.projectQueryPoint (projection_method_, nr_coeff_); + addProjectedPointNormal (index, proj.point, proj.normal, mls_result.curvature, projected_points, projected_points_normals, corresponding_input_indices); } else { // Sample the local plane for (int num_added = 0; num_added < num_points_to_add;) { - float u_disp = (*rng_uniform_distribution_) (), - v_disp = (*rng_uniform_distribution_) (); + double u = (*rng_uniform_distribution_) (); + double v = (*rng_uniform_distribution_) (); + // Check if inside circle; if not, try another coin flip - if (u_disp * u_disp + v_disp * v_disp > search_radius_ * search_radius_/4) + if (u * u + v * v > search_radius_ * search_radius_ / 4) continue; + MLSResult::MLSProjectionResults proj; + if (order_ > 1 && mls_result.num_neighbors >= 5 * nr_coeff_) + proj = mls_result.projectPointSimpleToPolynomialSurface (u, v); + else + proj = mls_result.projectPointToMLSPlane (u, v); - PointOutT projected_point; - pcl::Normal projected_normal; - projectPointToMLSSurface (u_disp, v_disp, u_axis, v_axis, plane_normal, point, - curvature, c_vec, - static_cast (nn_indices.size ()), - projected_point, projected_normal); - - projected_points.push_back (projected_point); - corresponding_input_indices.indices.push_back (index); - if (compute_normals_) - projected_points_normals.push_back (projected_normal); + addProjectedPointNormal (index, proj.point, proj.normal, mls_result.curvature, projected_points, projected_points_normals, corresponding_input_indices); - num_added ++; + num_added++; } } break; } - case (VOXEL_GRID_DILATION): - case (DISTINCT_CLOUD): - { - // Take all point pairs and sample space between them in a grid-fashion - // \note consider only point pairs with increasing indices - mls_result = MLSResult (point, plane_normal, u_axis, v_axis, c_vec, static_cast (nn_indices.size ()), curvature); + default: break; - } } } -////////////////////////////////////////////////////////////////////////////////////////////// template void -pcl::MovingLeastSquares::projectPointToMLSSurface (float &u_disp, float &v_disp, - Eigen::Vector3d &u, Eigen::Vector3d &v, - Eigen::Vector3d &plane_normal, - Eigen::Vector3d &mean, - float &curvature, - Eigen::VectorXd &c_vec, - int num_neighbors, - PointOutT &result_point, - pcl::Normal &result_normal) const +pcl::MovingLeastSquares::addProjectedPointNormal (int index, + const Eigen::Vector3d &point, + const Eigen::Vector3d &normal, + double curvature, + PointCloudOut &projected_points, + NormalCloud &projected_points_normals, + PointIndices &corresponding_input_indices) const { - double n_disp = 0.0f; - double d_u = 0.0f, d_v = 0.0f; + PointOutT aux; + aux.x = static_cast (point[0]); + aux.y = static_cast (point[1]); + aux.z = static_cast (point[2]); - // HARDCODED 5*nr_coeff_ to guarantee that the computed polynomial had a proper point set basis - if (polynomial_fit_ && num_neighbors >= 5*nr_coeff_ && pcl_isfinite (c_vec[0])) - { - // Compute the displacement along the normal using the fitted polynomial - // and compute the partial derivatives needed for estimating the normal - int j = 0; - float u_pow = 1.0f, v_pow = 1.0f, u_pow_prev = 1.0f, v_pow_prev = 1.0f; - for (int ui = 0; ui <= order_; ++ui) - { - v_pow = 1; - for (int vi = 0; vi <= order_ - ui; ++vi) - { - // Compute displacement along normal - n_disp += u_pow * v_pow * c_vec[j++]; + // Copy additional point information if available + copyMissingFields (input_->points[index], aux); - // Compute partial derivatives - if (ui >= 1) - d_u += c_vec[j-1] * ui * u_pow_prev * v_pow; - if (vi >= 1) - d_v += c_vec[j-1] * vi * u_pow * v_pow_prev; + projected_points.push_back (aux); + corresponding_input_indices.indices.push_back (index); - v_pow_prev = v_pow; - v_pow *= v_disp; - } - u_pow_prev = u_pow; - u_pow *= u_disp; - } + if (compute_normals_) + { + pcl::Normal aux_normal; + aux_normal.normal_x = static_cast (normal[0]); + aux_normal.normal_y = static_cast (normal[1]); + aux_normal.normal_z = static_cast (normal[2]); + aux_normal.curvature = curvature; + projected_points_normals.push_back (aux_normal); } - - result_point.x = static_cast (mean[0] + u[0] * u_disp + v[0] * v_disp + plane_normal[0] * n_disp); - result_point.y = static_cast (mean[1] + u[1] * u_disp + v[1] * v_disp + plane_normal[1] * n_disp); - result_point.z = static_cast (mean[2] + u[2] * u_disp + v[2] * v_disp + plane_normal[2] * n_disp); - - Eigen::Vector3d normal = plane_normal - d_u * u - d_v * v; - normal.normalize (); - - result_normal.normal_x = static_cast (normal[0]); - result_normal.normal_y = static_cast (normal[1]); - result_normal.normal_z = static_cast (normal[2]); - result_normal.curvature = curvature; } ////////////////////////////////////////////////////////////////////////////////////////////// @@ -468,71 +283,19 @@ pcl::MovingLeastSquares::performProcessing (PointCloudOut & // Compute the number of coefficients nr_coeff_ = (order_ + 1) * (order_ + 2) / 2; - // Allocate enough space to hold the results of nearest neighbor searches - // \note resize is irrelevant for a radiusSearch (). - std::vector nn_indices; - std::vector nn_sqr_dists; - - size_t mls_result_index = 0; - - // For all points - for (size_t cp = 0; cp < indices_->size (); ++cp) - { - // Get the initial estimates of point positions and their neighborhoods - if (!searchForNeighbors ((*indices_)[cp], nn_indices, nn_sqr_dists)) - continue; - - - // Check the number of nearest neighbors for normal estimation (and later - // for polynomial fit as well) - if (nn_indices.size () < 3) - continue; - - - PointCloudOut projected_points; - NormalCloud projected_points_normals; - // Get a plane approximating the local surface's tangent and project point onto it - int index = (*indices_)[cp]; - - if (upsample_method_ == VOXEL_GRID_DILATION || upsample_method_ == DISTINCT_CLOUD) - mls_result_index = index; // otherwise we give it a dummy location. - - computeMLSPointNormal (index, nn_indices, nn_sqr_dists, projected_points, projected_points_normals, *corresponding_input_indices_, mls_results_[mls_result_index]); - - - // Copy all information from the input cloud to the output points (not doing any interpolation) - for (size_t pp = 0; pp < projected_points.size (); ++pp) - copyMissingFields (input_->points[(*indices_)[cp]], projected_points[pp]); - - - // Append projected points to output - output.insert (output.end (), projected_points.begin (), projected_points.end ()); - if (compute_normals_) - normals_->insert (normals_->end (), projected_points_normals.begin (), projected_points_normals.end ()); - } - - // Perform the distinct-cloud or voxel-grid upsampling - performUpsampling (output); -} - -////////////////////////////////////////////////////////////////////////////////////////////// #ifdef _OPENMP -template void -pcl::MovingLeastSquaresOMP::performProcessing (PointCloudOut &output) -{ - // Compute the number of coefficients - nr_coeff_ = (order_ + 1) * (order_ + 2) / 2; - // (Maximum) number of threads - unsigned int threads = threads_ == 0 ? 1 : threads_; - + const unsigned int threads = threads_ == 0 ? 1 : threads_; // Create temporaries for each thread in order to avoid synchronization typename PointCloudOut::CloudVectorType projected_points (threads); typename NormalCloud::CloudVectorType projected_points_normals (threads); std::vector corresponding_input_indices (threads); +#endif // For all points +#ifdef _OPENMP #pragma omp parallel for schedule (dynamic,1000) num_threads (threads) +#endif for (int cp = 0; cp < static_cast (indices_->size ()); ++cp) { // Allocate enough space to hold the results of nearest neighbor searches @@ -541,56 +304,70 @@ pcl::MovingLeastSquaresOMP::performProcessing (PointCloudOu std::vector nn_sqr_dists; // Get the initial estimates of point positions and their neighborhoods - if (this->searchForNeighbors ((*indices_)[cp], nn_indices, nn_sqr_dists)) + if (searchForNeighbors ((*indices_)[cp], nn_indices, nn_sqr_dists)) { - // Check the number of nearest neighbors for normal estimation (and later - // for polynomial fit as well) + // Check the number of nearest neighbors for normal estimation (and later for polynomial fit as well) if (nn_indices.size () >= 3) { // This thread's ID (range 0 to threads-1) - int tn = omp_get_thread_num (); - +#ifdef _OPENMP + const int tn = omp_get_thread_num (); // Size of projected points before computeMLSPointNormal () adds points size_t pp_size = projected_points[tn].size (); +#else + PointCloudOut projected_points; + NormalCloud projected_points_normals; +#endif // Get a plane approximating the local surface's tangent and project point onto it - int index = (*indices_)[cp]; + const int index = (*indices_)[cp]; + size_t mls_result_index = 0; - - if (upsample_method_ == VOXEL_GRID_DILATION || upsample_method_ == DISTINCT_CLOUD) + if (cache_mls_results_) mls_result_index = index; // otherwise we give it a dummy location. - - this->computeMLSPointNormal (index, nn_indices, nn_sqr_dists, projected_points[tn], projected_points_normals[tn], corresponding_input_indices[tn], this->mls_results_[mls_result_index]); + +#ifdef _OPENMP + computeMLSPointNormal (index, nn_indices, projected_points[tn], projected_points_normals[tn], corresponding_input_indices[tn], mls_results_[mls_result_index]); // Copy all information from the input cloud to the output points (not doing any interpolation) for (size_t pp = pp_size; pp < projected_points[tn].size (); ++pp) - this->copyMissingFields (input_->points[(*indices_)[cp]], projected_points[tn][pp]); - } - } - } + copyMissingFields (input_->points[(*indices_)[cp]], projected_points[tn][pp]); +#else + computeMLSPointNormal (index, nn_indices, projected_points, projected_points_normals, *corresponding_input_indices_, mls_results_[mls_result_index]); + // Append projected points to output + output.insert (output.end (), projected_points.begin (), projected_points.end ()); + if (compute_normals_) + normals_->insert (normals_->end (), projected_points_normals.begin (), projected_points_normals.end ()); +#endif + } + } + } +#ifdef _OPENMP // Combine all threads' results into the output vectors for (unsigned int tn = 0; tn < threads; ++tn) { output.insert (output.end (), projected_points[tn].begin (), projected_points[tn].end ()); corresponding_input_indices_->indices.insert (corresponding_input_indices_->indices.end (), - corresponding_input_indices[tn].indices.begin (), corresponding_input_indices[tn].indices.end ()); + corresponding_input_indices[tn].indices.begin (), corresponding_input_indices[tn].indices.end ()); if (compute_normals_) normals_->insert (normals_->end (), projected_points_normals[tn].begin (), projected_points_normals[tn].end ()); } +#endif // Perform the distinct-cloud or voxel-grid upsampling - this->performUpsampling (output); + performUpsampling (output); } -#endif ////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::MovingLeastSquares::performUpsampling (PointCloudOut &output) { + if (upsample_method_ == DISTINCT_CLOUD) { + corresponding_input_indices_.reset (new PointIndices); for (size_t dp_i = 0; dp_i < distinct_cloud_->size (); ++dp_i) // dp_i = distinct_point_i { // Distinct cloud may have nan points, skip them @@ -610,30 +387,8 @@ pcl::MovingLeastSquares::performUpsampling (PointCloudOut & continue; Eigen::Vector3d add_point = distinct_cloud_->points[dp_i].getVector3fMap ().template cast (); - - float u_disp = static_cast ((add_point - mls_results_[input_index].mean).dot (mls_results_[input_index].u_axis)), - v_disp = static_cast ((add_point - mls_results_[input_index].mean).dot (mls_results_[input_index].v_axis)); - - PointOutT result_point; - pcl::Normal result_normal; - projectPointToMLSSurface (u_disp, v_disp, - mls_results_[input_index].u_axis, mls_results_[input_index].v_axis, - mls_results_[input_index].plane_normal, - mls_results_[input_index].mean, - mls_results_[input_index].curvature, - mls_results_[input_index].c_vec, - mls_results_[input_index].num_neighbors, - result_point, result_normal); - - // Copy additional point information if available - copyMissingFields (input_->points[input_index], result_point); - - // Store the id of the original point - corresponding_input_indices_->indices.push_back (input_index); - - output.push_back (result_point); - if (compute_normals_) - normals_->push_back (result_normal); + MLSResult::MLSProjectionResults proj = mls_results_[input_index].projectPoint (add_point, projection_method_, 5 * nr_coeff_); + addProjectedPointNormal (input_index, proj.point, proj.normal, mls_results_[input_index].curvature, output, *normals_, *corresponding_input_indices_); } } @@ -641,6 +396,8 @@ pcl::MovingLeastSquares::performUpsampling (PointCloudOut & // Then, project the newly obtained points to the MLS surface if (upsample_method_ == VOXEL_GRID_DILATION) { + corresponding_input_indices_.reset (new PointIndices); + MLSVoxelGrid voxel_grid (input_, indices_, voxel_size_); for (int iteration = 0; iteration < dilation_iteration_num_; ++iteration) voxel_grid.dilate (); @@ -667,54 +424,414 @@ pcl::MovingLeastSquares::performUpsampling (PointCloudOut & continue; Eigen::Vector3d add_point = p.getVector3fMap ().template cast (); - float u_disp = static_cast ((add_point - mls_results_[input_index].mean).dot (mls_results_[input_index].u_axis)), - v_disp = static_cast ((add_point - mls_results_[input_index].mean).dot (mls_results_[input_index].v_axis)); - - PointOutT result_point; - pcl::Normal result_normal; - projectPointToMLSSurface (u_disp, v_disp, - mls_results_[input_index].u_axis, mls_results_[input_index].v_axis, - mls_results_[input_index].plane_normal, - mls_results_[input_index].mean, - mls_results_[input_index].curvature, - mls_results_[input_index].c_vec, - mls_results_[input_index].num_neighbors, - result_point, result_normal); - - // Copy additional point information if available - copyMissingFields (input_->points[input_index], result_point); - - // Store the id of the original point - corresponding_input_indices_->indices.push_back (input_index); - - output.push_back (result_point); - - if (compute_normals_) - normals_->push_back (result_normal); + MLSResult::MLSProjectionResults proj = mls_results_[input_index].projectPoint (add_point, projection_method_, 5 * nr_coeff_); + addProjectedPointNormal (input_index, proj.point, proj.normal, mls_results_[input_index].curvature, output, *normals_, *corresponding_input_indices_); } } } ////////////////////////////////////////////////////////////////////////////////////////////// -template -pcl::MovingLeastSquares::MLSResult::MLSResult (const Eigen::Vector3d &a_mean, - const Eigen::Vector3d &a_plane_normal, - const Eigen::Vector3d &a_u, - const Eigen::Vector3d &a_v, - const Eigen::VectorXd a_c_vec, - const int a_num_neighbors, - const float &a_curvature) : - mean (a_mean), plane_normal (a_plane_normal), u_axis (a_u), v_axis (a_v), c_vec (a_c_vec), num_neighbors (a_num_neighbors), - curvature (a_curvature), valid (true) +pcl::MLSResult::MLSResult (const Eigen::Vector3d &a_query_point, + const Eigen::Vector3d &a_mean, + const Eigen::Vector3d &a_plane_normal, + const Eigen::Vector3d &a_u, + const Eigen::Vector3d &a_v, + const Eigen::VectorXd &a_c_vec, + const int a_num_neighbors, + const float a_curvature, + const int a_order) : + query_point (a_query_point), mean (a_mean), plane_normal (a_plane_normal), u_axis (a_u), v_axis (a_v), c_vec (a_c_vec), num_neighbors (a_num_neighbors), + curvature (a_curvature), order (a_order), valid (true) +{} + +void +pcl::MLSResult::getMLSCoordinates (const Eigen::Vector3d &pt, double &u, double &v, double &w) const { + Eigen::Vector3d delta = pt - mean; + u = delta.dot (u_axis); + v = delta.dot (v_axis); + w = delta.dot (plane_normal); +} + +void +pcl::MLSResult::getMLSCoordinates (const Eigen::Vector3d &pt, double &u, double &v) const +{ + Eigen::Vector3d delta = pt - mean; + u = delta.dot (u_axis); + v = delta.dot (v_axis); +} + +double +pcl::MLSResult::getPolynomialValue (const double u, const double v) const +{ + // Compute the polynomial's terms at the current point + // Example for second order: z = a + b*y + c*y^2 + d*x + e*x*y + f*x^2 + double u_pow, v_pow, result; + int j = 0; + u_pow = 1; + result = 0; + for (int ui = 0; ui <= order; ++ui) + { + v_pow = 1; + for (int vi = 0; vi <= order - ui; ++vi) + { + result += c_vec[j++] * u_pow * v_pow; + v_pow *= v; + } + u_pow *= u; + } + + return (result); +} + +pcl::MLSResult::PolynomialPartialDerivative +pcl::MLSResult::getPolynomialPartialDerivative (const double u, const double v) const +{ + // Compute the displacement along the normal using the fitted polynomial + // and compute the partial derivatives needed for estimating the normal + PolynomialPartialDerivative d; + Eigen::VectorXd u_pow (order + 2), v_pow (order + 2); + int j = 0; + + d.z = d.z_u = d.z_v = d.z_uu = d.z_vv = d.z_uv = 0; + u_pow (0) = v_pow (0) = 1; + for (int ui = 0; ui <= order; ++ui) + { + for (int vi = 0; vi <= order - ui; ++vi) + { + // Compute displacement along normal + d.z += u_pow (ui) * v_pow (vi) * c_vec[j]; + + // Compute partial derivatives + if (ui >= 1) + d.z_u += c_vec[j] * ui * u_pow (ui - 1) * v_pow (vi); + + if (vi >= 1) + d.z_v += c_vec[j] * vi * u_pow (ui) * v_pow (vi - 1); + + if (ui >= 1 && vi >= 1) + d.z_uv += c_vec[j] * ui * u_pow (ui - 1) * vi * v_pow (vi - 1); + + if (ui >= 2) + d.z_uu += c_vec[j] * ui * (ui - 1) * u_pow (ui - 2) * v_pow (vi); + + if (vi >= 2) + d.z_vv += c_vec[j] * vi * (vi - 1) * u_pow (ui) * v_pow (vi - 2); + + if (ui == 0) + v_pow (vi + 1) = v_pow (vi) * v; + + ++j; + } + u_pow (ui + 1) = u_pow (ui) * u; + } + + return (d); +} + +Eigen::Vector2f +pcl::MLSResult::calculatePrincipleCurvatures (const double u, const double v) const +{ + Eigen::Vector2f k (1e-5, 1e-5); + + // Note: this use the Monge Patch to derive the Gaussian curvature and Mean Curvature found here http://mathworld.wolfram.com/MongePatch.html + // Then: + // k1 = H + sqrt(H^2 - K) + // k1 = H - sqrt(H^2 - K) + if (order > 1 && c_vec.size () >= (order + 1) * (order + 2) / 2 && pcl_isfinite (c_vec[0])) + { + PolynomialPartialDerivative d = getPolynomialPartialDerivative (u, v); + double Z = 1 + d.z_u * d.z_u + d.z_v * d.z_v; + double Zlen = std::sqrt (Z); + double K = (d.z_uu * d.z_vv - d.z_uv * d.z_uv) / (Z * Z); + double H = ((1.0 + d.z_v * d.z_v) * d.z_uu - 2.0 * d.z_u * d.z_v * d.z_uv + (1.0 + d.z_u * d.z_u) * d.z_vv) / (2.0 * Zlen * Zlen * Zlen); + double disc2 = H * H - K; + assert (disc2 >= 0.0); + double disc = std::sqrt (disc2); + k[0] = H + disc; + k[1] = H - disc; + + if (std::abs (k[0]) > std::abs (k[1])) std::swap (k[0], k[1]); + } + else + { + PCL_ERROR ("No Polynomial fit data, unable to calculate the principle curvatures!\n"); + } + + return (k); +} + +pcl::MLSResult::MLSProjectionResults +pcl::MLSResult::projectPointOrthogonalToPolynomialSurface (const double u, const double v, const double w) const +{ + double gu = u; + double gv = v; + double gw = 0; + + MLSProjectionResults result; + result.normal = plane_normal; + if (order > 1 && c_vec.size () >= (order + 1) * (order + 2) / 2 && pcl_isfinite (c_vec[0])) + { + PolynomialPartialDerivative d = getPolynomialPartialDerivative (gu, gv); + gw = d.z; + double err_total; + double dist1 = std::abs (gw - w); + double dist2; + do + { + double e1 = (gu - u) + d.z_u * gw - d.z_u * w; + double e2 = (gv - v) + d.z_v * gw - d.z_v * w; + + double F1u = 1 + d.z_uu * gw + d.z_u * d.z_u - d.z_uu * w; + double F1v = d.z_uv * gw + d.z_u * d.z_v - d.z_uv * w; + + double F2u = d.z_uv * gw + d.z_v * d.z_u - d.z_uv * w; + double F2v = 1 + d.z_vv * gw + d.z_v * d.z_v - d.z_vv * w; + + Eigen::MatrixXd J (2, 2); + J (0, 0) = F1u; + J (0, 1) = F1v; + J (1, 0) = F2u; + J (1, 1) = F2v; + + Eigen::Vector2d err (e1, e2); + Eigen::Vector2d update = J.inverse () * err; + gu -= update (0); + gv -= update (1); + + d = getPolynomialPartialDerivative (gu, gv); + gw = d.z; + dist2 = std::sqrt ((gu - u) * (gu - u) + (gv - v) * (gv - v) + (gw - w) * (gw - w)); + + err_total = std::sqrt (e1 * e1 + e2 * e2); + + } while (err_total > 1e-8 && dist2 < dist1); + + if (dist2 > dist1) // the optimization was diverging reset the coordinates for simple projection + { + gu = u; + gv = v; + d = getPolynomialPartialDerivative (u, v); + gw = d.z; + } + + result.u = gu; + result.v = gv; + result.normal -= (d.z_u * u_axis + d.z_v * v_axis); + result.normal.normalize (); + } + + result.point = mean + gu * u_axis + gv * v_axis + gw * plane_normal; + + return (result); +} + +pcl::MLSResult::MLSProjectionResults +pcl::MLSResult::projectPointToMLSPlane (const double u, const double v) const +{ + MLSProjectionResults result; + result.u = u; + result.v = v; + result.normal = plane_normal; + result.point = mean + u * u_axis + v * v_axis; + + return (result); +} + +pcl::MLSResult::MLSProjectionResults +pcl::MLSResult::projectPointSimpleToPolynomialSurface (const double u, const double v) const +{ + MLSProjectionResults result; + double w = 0; + + result.u = u; + result.v = v; + result.normal = plane_normal; + + if (order > 1 && c_vec.size () >= (order + 1) * (order + 2) / 2 && pcl_isfinite (c_vec[0])) + { + PolynomialPartialDerivative d = getPolynomialPartialDerivative (u, v); + w = d.z; + result.normal -= (d.z_u * u_axis + d.z_v * v_axis); + result.normal.normalize (); + } + + result.point = mean + u * u_axis + v * v_axis + w * plane_normal; + + return (result); +} + +pcl::MLSResult::MLSProjectionResults +pcl::MLSResult::projectPoint (const Eigen::Vector3d &pt, ProjectionMethod method, int required_neighbors) const +{ + double u, v, w; + getMLSCoordinates (pt, u, v, w); + + MLSResult::MLSProjectionResults proj; + if (order > 1 && num_neighbors >= required_neighbors && pcl_isfinite (c_vec[0]) && method != NONE) + { + if (method == ORTHOGONAL) + proj = projectPointOrthogonalToPolynomialSurface (u, v, w); + else // SIMPLE + proj = projectPointSimpleToPolynomialSurface (u, v); + } + else + { + proj = projectPointToMLSPlane (u, v); + } + + return (proj); +} + +pcl::MLSResult::MLSProjectionResults +pcl::MLSResult::projectQueryPoint (ProjectionMethod method, int required_neighbors) const +{ + MLSResult::MLSProjectionResults proj; + if (order > 1 && num_neighbors >= required_neighbors && pcl_isfinite (c_vec[0]) && method != NONE) + { + if (method == ORTHOGONAL) + { + double u, v, w; + getMLSCoordinates (query_point, u, v, w); + proj = projectPointOrthogonalToPolynomialSurface (u, v, w); + } + else // SIMPLE + { + // Projection onto MLS surface along Darboux normal to the height at (0,0) + proj.point = mean + (c_vec[0] * plane_normal); + + // Compute tangent vectors using the partial derivates evaluated at (0,0) which is c_vec[order_+1] and c_vec[1] + proj.normal = plane_normal - c_vec[order + 1] * u_axis - c_vec[1] * v_axis; + proj.normal.normalize (); + } + } + else + { + proj.normal = plane_normal; + proj.point = mean; + } + + return (proj); +} + +template void +pcl::MLSResult::computeMLSSurface (const pcl::PointCloud &cloud, + int index, + const std::vector &nn_indices, + double search_radius, + int polynomial_order, + boost::function weight_func) +{ + // Compute the plane coefficients + EIGEN_ALIGN16 Eigen::Matrix3d covariance_matrix; + Eigen::Vector4d xyz_centroid; + + // Estimate the XYZ centroid + pcl::compute3DCentroid (cloud, nn_indices, xyz_centroid); + + // Compute the 3x3 covariance matrix + pcl::computeCovarianceMatrix (cloud, nn_indices, xyz_centroid, covariance_matrix); + EIGEN_ALIGN16 Eigen::Vector3d::Scalar eigen_value; + EIGEN_ALIGN16 Eigen::Vector3d eigen_vector; + Eigen::Vector4d model_coefficients (0, 0, 0, 0); + pcl::eigen33 (covariance_matrix, eigen_value, eigen_vector); + model_coefficients.head<3> ().matrix () = eigen_vector; + model_coefficients[3] = -1 * model_coefficients.dot (xyz_centroid); + + // Projected query point + valid = true; + query_point = cloud.points[index].getVector3fMap ().template cast (); + double distance = query_point.dot (model_coefficients.head<3> ()) + model_coefficients[3]; + mean = query_point - distance * model_coefficients.head<3> (); + + curvature = covariance_matrix.trace (); + // Compute the curvature surface change + if (curvature != 0) + curvature = std::abs (eigen_value / curvature); + + // Get a copy of the plane normal easy access + plane_normal = model_coefficients.head<3> (); + + // Local coordinate system (Darboux frame) + v_axis = plane_normal.unitOrthogonal (); + u_axis = plane_normal.cross (v_axis); + + // Perform polynomial fit to update point and normal + //////////////////////////////////////////////////// + num_neighbors = static_cast (nn_indices.size ()); + order = polynomial_order; + if (order > 1) + { + int nr_coeff = (order + 1) * (order + 2) / 2; + + if (num_neighbors >= nr_coeff) + { + // Note: The max_sq_radius parameter is only used if weight_func was not defined + double max_sq_radius = 1; + if (weight_func == 0) + { + max_sq_radius = search_radius * search_radius; + weight_func = boost::bind (&pcl::MLSResult::computeMLSWeight, this, _1, max_sq_radius); + } + + // Allocate matrices and vectors to hold the data used for the polynomial fit + Eigen::VectorXd weight_vec (num_neighbors); + Eigen::MatrixXd P (nr_coeff, num_neighbors); + Eigen::VectorXd f_vec (num_neighbors); + Eigen::MatrixXd P_weight; // size will be (nr_coeff_, nn_indices.size ()); + Eigen::MatrixXd P_weight_Pt (nr_coeff, nr_coeff); + + // Update neighborhood, since point was projected, and computing relative + // positions. Note updating only distances for the weights for speed + std::vector > de_meaned (num_neighbors); + for (size_t ni = 0; ni < (size_t) num_neighbors; ++ni) + { + de_meaned[ni][0] = cloud.points[nn_indices[ni]].x - mean[0]; + de_meaned[ni][1] = cloud.points[nn_indices[ni]].y - mean[1]; + de_meaned[ni][2] = cloud.points[nn_indices[ni]].z - mean[2]; + weight_vec (ni) = weight_func (de_meaned[ni].dot (de_meaned[ni])); + } + + // Go through neighbors, transform them in the local coordinate system, + // save height and the evaluation of the polynome's terms + double u_coord, v_coord, u_pow, v_pow; + for (size_t ni = 0; ni < (size_t) num_neighbors; ++ni) + { + // Transforming coordinates + u_coord = de_meaned[ni].dot (u_axis); + v_coord = de_meaned[ni].dot (v_axis); + f_vec (ni) = de_meaned[ni].dot (plane_normal); + + // Compute the polynomial's terms at the current point + int j = 0; + u_pow = 1; + for (int ui = 0; ui <= order; ++ui) + { + v_pow = 1; + for (int vi = 0; vi <= order - ui; ++vi) + { + P (j++, ni) = u_pow * v_pow; + v_pow *= v_coord; + } + u_pow *= u_coord; + } + } + + // Computing coefficients + P_weight = P * weight_vec.asDiagonal (); + P_weight_Pt = P_weight * P.transpose (); + c_vec = P_weight * f_vec; + P_weight_Pt.llt ().solveInPlace (c_vec); + } + } } ////////////////////////////////////////////////////////////////////////////////////////////// template pcl::MovingLeastSquares::MLSVoxelGrid::MLSVoxelGrid (PointCloudInConstPtr& cloud, - IndicesPtr &indices, - float voxel_size) : - voxel_grid_ (), bounding_min_ (), bounding_max_ (), data_size_ (), voxel_size_ (voxel_size) + IndicesPtr &indices, + float voxel_size) : + voxel_grid_ (), bounding_min_ (), bounding_max_ (), data_size_ (), voxel_size_ (voxel_size) { pcl::getMinMax3D (*cloud, *indices, bounding_min_, bounding_max_); @@ -776,11 +893,7 @@ pcl::MovingLeastSquares::copyMissingFields (const PointInT point_out.z = temp.z; } - #define PCL_INSTANTIATE_MovingLeastSquares(T,OutT) template class PCL_EXPORTS pcl::MovingLeastSquares; - -#ifdef _OPENMP #define PCL_INSTANTIATE_MovingLeastSquaresOMP(T,OutT) template class PCL_EXPORTS pcl::MovingLeastSquaresOMP; -#endif #endif // PCL_SURFACE_IMPL_MLS_H_ diff --git a/surface/include/pcl/surface/marching_cubes.h b/surface/include/pcl/surface/marching_cubes.h index a640ad99..d20f5c97 100644 --- a/surface/include/pcl/surface/marching_cubes.h +++ b/surface/include/pcl/surface/marching_cubes.h @@ -374,9 +374,13 @@ namespace pcl typedef typename pcl::KdTree KdTree; typedef typename pcl::KdTree::Ptr KdTreePtr; - /** \brief Constructor. */ - MarchingCubes (); + MarchingCubes (const float percentage_extend_grid = 0.0f, + const float iso_level = 0.0f) : + percentage_extend_grid_ (percentage_extend_grid), + iso_level_ (iso_level) + { + } /** \brief Destructor. */ virtual ~MarchingCubes (); @@ -403,7 +407,6 @@ namespace pcl setGridResolution (int res_x, int res_y, int res_z) { res_x_ = res_x; res_y_ = res_y; res_z_ = res_z; } - /** \brief Method to get the marching cubes grid resolution. * \param[in] res_x the resolution of the grid along the x-axis * \param[in] res_y the resolution of the grid along the y-axis @@ -437,8 +440,12 @@ namespace pcl /** \brief The grid resolution */ int res_x_, res_y_, res_z_; - /** \brief Min and max data points. */ - Eigen::Vector4f min_p_, max_p_; + /** \brief bounding box */ + Eigen::Array3f upper_boundary_; + Eigen::Array3f lower_boundary_; + + /** \brief size of voxels */ + Eigen::Array3f size_voxel_; /** \brief Parameter that defines how much free space should be left inside the grid between * the bounding box of the point cloud and the grid limits, as a percentage of the bounding box.*/ @@ -447,7 +454,8 @@ namespace pcl /** \brief The iso level to be extracted. */ float iso_level_; - /** \brief Convert the point cloud into voxel data. */ + /** \brief Convert the point cloud into voxel data. + */ virtual void voxelizeData () = 0; @@ -466,13 +474,14 @@ namespace pcl * \param leaf_node the leaf node to be checked * \param index_3d the 3d index of the leaf node to be checked * \param cloud point cloud to store the vertices of the polygon - */ + */ void - createSurface (std::vector &leaf_node, - Eigen::Vector3i &index_3d, + createSurface (const std::vector &leaf_node, + const Eigen::Vector3i &index_3d, pcl::PointCloud &cloud); - /** \brief Get the bounding box for the input data points. */ + /** \brief Get the bounding box for the input data points. + */ void getBoundingBox (); diff --git a/surface/include/pcl/surface/marching_cubes_hoppe.h b/surface/include/pcl/surface/marching_cubes_hoppe.h index 5ff5a8d7..6f14d139 100644 --- a/surface/include/pcl/surface/marching_cubes_hoppe.h +++ b/surface/include/pcl/surface/marching_cubes_hoppe.h @@ -61,8 +61,9 @@ namespace pcl using MarchingCubes::res_x_; using MarchingCubes::res_y_; using MarchingCubes::res_z_; - using MarchingCubes::min_p_; - using MarchingCubes::max_p_; + using MarchingCubes::size_voxel_; + using MarchingCubes::upper_boundary_; + using MarchingCubes::lower_boundary_; typedef typename pcl::PointCloud::Ptr PointCloudPtr; @@ -71,15 +72,43 @@ namespace pcl /** \brief Constructor. */ - MarchingCubesHoppe (); + MarchingCubesHoppe (const float dist_ignore = -1.0f, + const float percentage_extend_grid = 0.0f, + const float iso_level = 0.0f) : + MarchingCubes (percentage_extend_grid, iso_level), + dist_ignore_ (dist_ignore) + { + } /** \brief Destructor. */ ~MarchingCubesHoppe (); - /** \brief Convert the point cloud into voxel data. */ + /** \brief Convert the point cloud into voxel data. + */ void voxelizeData (); + /** \brief Method that sets the distance for ignoring voxels which are far from point cloud. + * If the distance is negative, then the distance functions would be calculated in all voxels; + * otherwise, only voxels with distance lower than dist_ignore would be involved in marching cube. + * \param[in] threshold of distance. Default value is -1.0. Set to negative if all voxels are + * to be involved. + */ + inline void + setDistanceIgnore (const float dist_ignore) + { dist_ignore_ = dist_ignore; } + + /** \brief get the distance for ignoring voxels which are far from point cloud. + * */ + inline float + getDistanceIgnore () const + { return dist_ignore_; } + + protected: + /** \brief ignore the distance function + * if it is negative + * or distance between voxel centroid and point are larger that it. */ + float dist_ignore_; public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW diff --git a/surface/include/pcl/surface/marching_cubes_rbf.h b/surface/include/pcl/surface/marching_cubes_rbf.h index 8b99dd8e..a0a49a8a 100644 --- a/surface/include/pcl/surface/marching_cubes_rbf.h +++ b/surface/include/pcl/surface/marching_cubes_rbf.h @@ -63,8 +63,9 @@ namespace pcl using MarchingCubes::res_x_; using MarchingCubes::res_y_; using MarchingCubes::res_z_; - using MarchingCubes::min_p_; - using MarchingCubes::max_p_; + using MarchingCubes::size_voxel_; + using MarchingCubes::upper_boundary_; + using MarchingCubes::lower_boundary_; typedef typename pcl::PointCloud::Ptr PointCloudPtr; @@ -73,12 +74,19 @@ namespace pcl /** \brief Constructor. */ - MarchingCubesRBF (); + MarchingCubesRBF (const float off_surface_epsilon = 0.1f, + const float percentage_extend_grid = 0.0f, + const float iso_level = 0.0f) : + MarchingCubes (percentage_extend_grid, iso_level), + off_surface_epsilon_ (off_surface_epsilon) + { + } /** \brief Destructor. */ ~MarchingCubesRBF (); - /** \brief Convert the point cloud into voxel data. */ + /** \brief Convert the point cloud into voxel data. + */ void voxelizeData (); diff --git a/surface/include/pcl/surface/mls.h b/surface/include/pcl/surface/mls.h index 8aeb9a2f..2e14f2ba 100644 --- a/surface/include/pcl/surface/mls.h +++ b/surface/include/pcl/surface/mls.h @@ -49,20 +49,205 @@ #include #include #include +#include namespace pcl { - /** \brief MovingLeastSquares represent an implementation of the MLS (Moving Least Squares) algorithm - * for data smoothing and improved normal estimation. It also contains methods for upsampling the + + /** \brief Data structure used to store the results of the MLS fitting */ + struct MLSResult + { + enum ProjectionMethod + { + NONE, /**< \brief Project to the mls plane. */ + SIMPLE, /**< \brief Project along the mls plane normal to the polynomial surface. */ + ORTHOGONAL /**< \brief Project to the closest point on the polynonomial surface. */ + }; + + /** \brief Data structure used to store the MLS polynomial partial derivatives */ + struct PolynomialPartialDerivative + { + double z; /**< \brief The z component of the polynomial evaluated at z(u, v). */ + double z_u; /**< \brief The partial derivative dz/du. */ + double z_v; /**< \brief The partial derivative dz/dv. */ + double z_uu; /**< \brief The partial derivative d^2z/du^2. */ + double z_vv; /**< \brief The partial derivative d^2z/dv^2. */ + double z_uv; /**< \brief The partial derivative d^2z/dudv. */ + }; + + /** \brief Data structure used to store the MLS projection results */ + struct MLSProjectionResults + { + MLSProjectionResults () : u (0), v (0) {} + + double u; /**< \brief The u-coordinate of the projected point in local MLS frame. */ + double v; /**< \brief The u-coordinate of the projected point in local MLS frame. */ + Eigen::Vector3d point; /**< \brief The projected point. */ + Eigen::Vector3d normal; /**< \brief The projected point's normal. */ + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + }; + + inline + MLSResult () : num_neighbors (0), curvature (0.0f), order (0), valid (false) {} + + inline + MLSResult (const Eigen::Vector3d &a_query_point, + const Eigen::Vector3d &a_mean, + const Eigen::Vector3d &a_plane_normal, + const Eigen::Vector3d &a_u, + const Eigen::Vector3d &a_v, + const Eigen::VectorXd &a_c_vec, + const int a_num_neighbors, + const float a_curvature, + const int a_order); + + /** \brief Given a point calculate it's 3D location in the MLS frame. + * \param[in] pt The point + * \param[out] u The u-coordinate of the point in local MLS frame. + * \param[out] v The v-coordinate of the point in local MLS frame. + * \param[out] w The w-coordinate of the point in local MLS frame. + */ + inline void + getMLSCoordinates (const Eigen::Vector3d &pt, double &u, double &v, double &w) const; + + /** \brief Given a point calculate it's 2D location in the MLS frame. + * \param[in] pt The point + * \param[out] u The u-coordinate of the point in local MLS frame. + * \param[out] v The v-coordinate of the point in local MLS frame. + */ + inline void + getMLSCoordinates (const Eigen::Vector3d &pt, double &u, double &v) const; + + /** \brief Calculate the polynomial + * \param[in] u The u-coordinate of the point in local MLS frame. + * \param[in] v The v-coordinate of the point in local MLS frame. + * \return The polynomial value at the provide uv coordinates. + */ + inline double + getPolynomialValue (const double u, const double v) const; + + /** \brief Calculate the polynomial's first and second partial derivatives. + * \param[in] u The u-coordinate of the point in local MLS frame. + * \param[in] v The v-coordinate of the point in local MLS frame. + * \return The polynomial partial derivatives at the provide uv coordinates. + */ + inline PolynomialPartialDerivative + getPolynomialPartialDerivative (const double u, const double v) const; + + /** \brief Calculate the principle curvatures using the polynomial surface. + * \param[in] u The u-coordinate of the point in local MLS frame. + * \param[in] v The v-coordinate of the point in local MLS frame. + * \return The principle curvature [k1, k2] at the provided ub coordinates. + * \note If an error occurs the MLS_MINIMUM_PRINCIPLE_CURVATURE is returned. + */ + inline Eigen::Vector2f + calculatePrincipleCurvatures (const double u, const double v) const; + + /** \brief Project a point orthogonal to the polynomial surface. + * \param[in] u The u-coordinate of the point in local MLS frame. + * \param[in] v The v-coordinate of the point in local MLS frame. + * \param[in] w The w-coordinate of the point in local MLS frame. + * \return The MLSProjectionResults for the input data. + * \note If the MLSResults does not contain polynomial data it projects the point onto the mls plane. + * \note If the optimization diverges it performs a simple projection on to the polynomial surface. + * \note This was implemented based on this https://math.stackexchange.com/questions/1497093/shortest-distance-between-point-and-surface + */ + inline MLSProjectionResults + projectPointOrthogonalToPolynomialSurface (const double u, const double v, const double w) const; + + /** \brief Project a point onto the MLS plane. + * \param[in] u The u-coordinate of the point in local MLS frame. + * \param[in] v The v-coordinate of the point in local MLS frame. + * \return The MLSProjectionResults for the input data. + */ + inline MLSProjectionResults + projectPointToMLSPlane (const double u, const double v) const; + + /** \brief Project a point along the MLS plane normal to the polynomial surface. + * \param[in] u The u-coordinate of the point in local MLS frame. + * \param[in] v The v-coordinate of the point in local MLS frame. + * \return The MLSProjectionResults for the input data. + * \note If the MLSResults does not contain polynomial data it projects the point onto the mls plane. + */ + inline MLSProjectionResults + projectPointSimpleToPolynomialSurface (const double u, const double v) const; + + /** + * \brief Project a point using the specified method. + * \param[in] pt The point to be project. + * \param[in] method The projection method to be used. + * \param[in] required_neighbors The minimum number of neighbors required. + * \note If required_neighbors then any number of neighbors is allowed. + * \note If required_neighbors is not satisfied it projects to the mls plane. + * \return The MLSProjectionResults for the input data. + */ + inline MLSProjectionResults + projectPoint (const Eigen::Vector3d &pt, ProjectionMethod method, int required_neighbors = 0) const; + + /** + * \brief Project the query point used to generate the mls surface about using the specified method. + * \param[in] method The projection method to be used. + * \param[in] required_neighbors The minimum number of neighbors required. + * \note If required_neighbors then any number of neighbors is allowed. + * \note If required_neighbors is not satisfied it projects to the mls plane. + * \return The MLSProjectionResults for the input data. + */ + inline MLSProjectionResults + projectQueryPoint (ProjectionMethod method, int required_neighbors = 0) const; + + /** \brief Smooth a given point and its neighborghood using Moving Least Squares. + * \param[in] index the index of the query point in the input cloud + * \param[in] nn_indices the set of nearest neighbors indices for pt + * \param[in] search_radius the search radius used to find nearest neighbors for pt + * \param[in] polynomial_order the order of the polynomial to fit to the nearest neighbors + * \param[in] weight_func defines the weight function for the polynomial fit + */ + template void + computeMLSSurface (const pcl::PointCloud &cloud, + int index, + const std::vector &nn_indices, + double search_radius, + int polynomial_order = 2, + boost::function weight_func = 0); + + Eigen::Vector3d query_point; /**< \brief The query point about which the mls surface was generated */ + Eigen::Vector3d mean; /**< \brief The mean point of all the neighbors. */ + Eigen::Vector3d plane_normal; /**< \brief The normal of the local plane of the query point. */ + Eigen::Vector3d u_axis; /**< \brief The axis corresponding to the u-coordinates of the local plane of the query point. */ + Eigen::Vector3d v_axis; /**< \brief The axis corresponding to the v-coordinates of the local plane of the query point. */ + Eigen::VectorXd c_vec; /**< \brief The polynomial coefficients Example: z = c_vec[0] + c_vec[1]*v + c_vec[2]*v^2 + c_vec[3]*u + c_vec[4]*u*v + c_vec[5]*u^2 */ + int num_neighbors; /**< \brief The number of neighbors used to create the mls surface. */ + float curvature; /**< \brief The curvature at the query point. */ + int order; /**< \brief The order of the polynomial. If order > 1 then use polynomial fit */ + bool valid; /**< \brief If True, the mls results data is valid, otherwise False. */ + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + private: + /** + * \brief The default weight function used when fitting a polynomial surface + * \param sq_dist the squared distance from a point to origin of the mls frame + * \param sq_mls_radius the squraed mls search radius used + * \return The weight for a point at squared distance from the origin of the mls frame + */ + inline + double computeMLSWeight (const double sq_dist, const double sq_mls_radius) { return (exp (-sq_dist / sq_mls_radius)); } + + }; + + /** \brief MovingLeastSquares represent an implementation of the MLS (Moving Least Squares) algorithm + * for data smoothing and improved normal estimation. It also contains methods for upsampling the * resulting cloud based on the parametric fit. - * Reference paper: "Computing and Rendering Point Set Surfaces" by Marc Alexa, Johannes Behr, + * Reference paper: "Computing and Rendering Point Set Surfaces" by Marc Alexa, Johannes Behr, * Daniel Cohen-Or, Shachar Fleishman, David Levin and Claudio T. Silva * www.sci.utah.edu/~shachar/Publications/crpss.pdf - * \author Zoltan Csaba Marton, Radu B. Rusu, Alexandru E. Ichim, Suat Gedikli + * \note There is a parallelized version of the processing step, using the OpenMP standard. + * Compared to the standard version, an overhead is incurred in terms of runtime and memory usage. + * The upsampling methods DISTINCT_CLOUD and VOXEL_GRID_DILATION are not parallelized completely, + * i.e. parts of the algorithm run on a single thread only. + * \author Zoltan Csaba Marton, Radu B. Rusu, Alexandru E. Ichim, Suat Gedikli, Robert Huitl * \ingroup surface */ template - class MovingLeastSquares: public CloudSurfaceProcessing + class MovingLeastSquares : public CloudSurfaceProcessing { public: typedef boost::shared_ptr > Ptr; @@ -89,7 +274,22 @@ namespace pcl typedef boost::function &, std::vector &)> SearchMethod; - enum UpsamplingMethod {NONE, DISTINCT_CLOUD, SAMPLE_LOCAL_PLANE, RANDOM_UNIFORM_DENSITY, VOXEL_GRID_DILATION}; + enum UpsamplingMethod + { + NONE, /**< \brief No upsampling will be done, only the input points will be projected + to their own MLS surfaces. */ + DISTINCT_CLOUD, /**< \brief Project the points of the distinct cloud to the MLS surface. */ + SAMPLE_LOCAL_PLANE, /**< \brief The local plane of each input point will be sampled in a circular fashion + using the \ref upsampling_radius_ and the \ref upsampling_step_ parameters. */ + RANDOM_UNIFORM_DENSITY, /**< \brief The local plane of each input point will be sampled using an uniform random + distribution such that the density of points is constant throughout the + cloud - given by the \ref desired_num_points_in_radius_ parameter. */ + VOXEL_GRID_DILATION /**< \brief The input cloud will be inserted into a voxel grid with voxels of + size \ref voxel_size_; this voxel grid will be dilated \ref dilation_iteration_num_ + times and the resulting points will be projected to the MLS surface + of the closest point in the input cloud; the result is a point cloud + with filled holes and a constant point density. */ + }; /** \brief Empty constructor. */ MovingLeastSquares () : CloudSurfaceProcessing (), @@ -98,7 +298,6 @@ namespace pcl search_method_ (), tree_ (), order_ (2), - polynomial_fit_ (true), search_radius_ (0.0), sqr_gauss_param_ (0.0), compute_normals_ (false), @@ -106,7 +305,10 @@ namespace pcl upsampling_radius_ (0.0), upsampling_step_ (0.0), desired_num_points_in_radius_ (0), + cache_mls_results_ (true), mls_results_ (), + projection_method_ (MLSResult::SIMPLE), + threads_ (1), voxel_size_ (1.0), dilation_iteration_num_ (0), nr_coeff_ (), @@ -114,7 +316,7 @@ namespace pcl rng_alg_ (), rng_uniform_distribution_ () {}; - + /** \brief Empty destructor */ virtual ~MovingLeastSquares () {} @@ -138,69 +340,69 @@ namespace pcl } /** \brief Get a pointer to the search method used. */ - inline KdTreePtr - getSearchMethod () { return (tree_); } + inline KdTreePtr + getSearchMethod () const { return (tree_); } /** \brief Set the order of the polynomial to be fit. * \param[in] order the order of the polynomial + * \note Setting order > 1 indicates using a polynomial fit. */ - inline void + inline void setPolynomialOrder (int order) { order_ = order; } /** \brief Get the order of the polynomial to be fit. */ - inline int - getPolynomialOrder () { return (order_); } + inline int + getPolynomialOrder () const { return (order_); } /** \brief Sets whether the surface and normal are approximated using a polynomial, or only via tangent estimation. * \param[in] polynomial_fit set to true for polynomial fit */ - inline void - setPolynomialFit (bool polynomial_fit) { polynomial_fit_ = polynomial_fit; } + PCL_DEPRECATED ("[pcl::surface::MovingLeastSquares::setPolynomialFit] setPolynomialFit is deprecated. Please use setPolynomialOrder instead.") + inline void + setPolynomialFit (bool polynomial_fit) + { + if (polynomial_fit) + { + if (order_ < 2) + { + order_ = 2; + } + } + else + { + order_ = 0; + } + } /** \brief Get the polynomial_fit value (true if the surface and normal are approximated using a polynomial). */ - inline bool - getPolynomialFit () { return (polynomial_fit_); } + PCL_DEPRECATED ("[pcl::surface::MovingLeastSquares::getPolynomialFit] getPolynomialFit is deprecated. Please use getPolynomialOrder instead.") + inline bool + getPolynomialFit () const { return (order_ > 1); } /** \brief Set the sphere radius that is to be used for determining the k-nearest neighbors used for fitting. * \param[in] radius the sphere radius that is to contain all k-nearest neighbors * \note Calling this method resets the squared Gaussian parameter to radius * radius ! */ - inline void + inline void setSearchRadius (double radius) { search_radius_ = radius; sqr_gauss_param_ = search_radius_ * search_radius_; } /** \brief Get the sphere radius used for determining the k-nearest neighbors. */ - inline double - getSearchRadius () { return (search_radius_); } + inline double + getSearchRadius () const { return (search_radius_); } /** \brief Set the parameter used for distance based weighting of neighbors (the square of the search radius works * best in general). * \param[in] sqr_gauss_param the squared Gaussian parameter */ - inline void + inline void setSqrGaussParam (double sqr_gauss_param) { sqr_gauss_param_ = sqr_gauss_param; } /** \brief Get the parameter for distance based weighting of neighbors. */ - inline double + inline double getSqrGaussParam () const { return (sqr_gauss_param_); } /** \brief Set the upsampling method to be used * \param method - * \note Options are: * NONE - no upsampling will be done, only the input points will be projected to their own - * MLS surfaces - * * DISTINCT_CLOUD - will project the points of the distinct cloud to the closest point on - * the MLS surface - * * SAMPLE_LOCAL_PLANE - the local plane of each input point will be sampled in a circular - * fashion using the \ref upsampling_radius_ and the \ref upsampling_step_ - * parameters - * * RANDOM_UNIFORM_DENSITY - the local plane of each input point will be sampled using an - * uniform random distribution such that the density of points is - * constant throughout the cloud - given by the \ref desired_num_points_in_radius_ - * parameter - * * VOXEL_GRID_DILATION - the input cloud will be inserted into a voxel grid with voxels of - * size \ref voxel_size_; this voxel grid will be dilated \ref dilation_iteration_num_ - * times and the resulting points will be projected to the MLS surface - * of the closest point in the input cloud; the result is a point cloud - * with filled holes and a constant point density */ inline void setUpsamplingMethod (UpsamplingMethod method) { upsample_method_ = method; } @@ -211,7 +413,7 @@ namespace pcl /** \brief Get the distinct cloud used for the DISTINCT_CLOUD upsampling method. */ inline PointCloudInConstPtr - getDistinctCloud () { return distinct_cloud_; } + getDistinctCloud () const { return (distinct_cloud_); } /** \brief Set the radius of the circle in the local point plane that will be sampled @@ -225,7 +427,7 @@ namespace pcl * \note Used only in the case of SAMPLE_LOCAL_PLANE upsampling */ inline double - getUpsamplingRadius () { return upsampling_radius_; } + getUpsamplingRadius () const { return (upsampling_radius_); } /** \brief Set the step size for the local plane sampling * \note Used only in the case of SAMPLE_LOCAL_PLANE upsampling @@ -239,7 +441,7 @@ namespace pcl * \note Used only in the case of SAMPLE_LOCAL_PLANE upsampling */ inline double - getUpsamplingStepSize () { return upsampling_step_; } + getUpsamplingStepSize () const { return (upsampling_step_); } /** \brief Set the parameter that specifies the desired number of points within the search radius * \note Used only in the case of RANDOM_UNIFORM_DENSITY upsampling @@ -254,7 +456,7 @@ namespace pcl * \note Used only in the case of RANDOM_UNIFORM_DENSITY upsampling */ inline int - getPointDensity () { return desired_num_points_in_radius_; } + getPointDensity () const { return (desired_num_points_in_radius_); } /** \brief Set the voxel size for the voxel grid * \note Used only in the VOXEL_GRID_DILATION upsampling method @@ -268,7 +470,7 @@ namespace pcl * \note Used only in the VOXEL_GRID_DILATION upsampling method */ inline float - getDilationVoxelSize () { return voxel_size_; } + getDilationVoxelSize () const { return (voxel_size_); } /** \brief Set the number of dilation steps of the voxel grid * \note Used only in the VOXEL_GRID_DILATION upsampling method @@ -281,19 +483,59 @@ namespace pcl * \note Used only in the VOXEL_GRID_DILATION upsampling method */ inline int - getDilationIterations () { return dilation_iteration_num_; } + getDilationIterations () const { return (dilation_iteration_num_); } + + /** \brief Set whether the mls results should be stored for each point in the input cloud + * \param[in] True if the mls results should be stored, otherwise false. + * \note The cache_mls_results_ is forced to true when using upsampling method VOXEL_GRID_DILATION or DISTINCT_CLOUD. + * \note If memory consumption is a concern set to false when not using upsampling method VOXEL_GRID_DILATION or DISTINCT_CLOUD. + */ + inline void + setCacheMLSResults (bool cache_mls_results) { cache_mls_results_ = cache_mls_results; } + + /** \brief Get the cache_mls_results_ value (True if the mls results should be stored, otherwise false). */ + inline bool + getCacheMLSResults () const { return (cache_mls_results_); } + + /** \brief Set the method to be used when projection the point on to the MLS surface. + * \param method + * \note This is only used when polynomial fit is enabled. + */ + inline void + setProjectionMethod (MLSResult::ProjectionMethod method) { projection_method_ = method; } + + + /** \brief Get the current projection method being used. */ + inline MLSResult::ProjectionMethod + getProjectionMethod () const { return (projection_method_); } + + /** \brief Get the MLSResults for input cloud + * \note The results are only stored if setCacheMLSResults(true) was called or when using the upsampling method DISTINCT_CLOUD or VOXEL_GRID_DILATION. + * \note This vector is align with the input cloud indices, so use getCorrespondingIndices to get the correct results when using output cloud indices. + */ + inline const std::vector& + getMLSResults () const { return (mls_results_); } + + /** \brief Set the maximum number of threads to use + * \param threads the maximum number of hardware threads to use (0 sets the value to 1) + */ + inline void + setNumberOfThreads (unsigned int threads = 1) + { + threads_ = threads; + } /** \brief Base method for surface reconstruction for all points given in * \param[out] output the resultant reconstructed surface model */ - void + void process (PointCloudOut &output); - /** \brief Get the set of indices with each point in output having the + /** \brief Get the set of indices with each point in output having the * corresponding point in input */ inline PointIndicesPtr - getCorrespondingIndices () { return (corresponding_input_indices_); } + getCorrespondingIndices () const { return (corresponding_input_indices_); } protected: /** \brief The point cloud that will hold the estimated normals, if set. */ @@ -311,9 +553,6 @@ namespace pcl /** \brief The order of the polynomial to be fit. */ int order_; - /** True if the surface and normal be approximated using a polynomial, false if tangent estimation is sufficient. */ - bool polynomial_fit_; - /** \brief The nearest neighbors search radius for each point. */ double search_radius_; @@ -341,35 +580,23 @@ namespace pcl */ int desired_num_points_in_radius_; - - /** \brief Data structure used to store the results of the MLS fitting - * \note Used only in the case of VOXEL_GRID_DILATION or DISTINCT_CLOUD upsampling + /** \brief True if the mls results for the input cloud should be stored + * \note This is forced to true when using upsampling methods VOXEL_GRID_DILATION or DISTINCT_CLOUD. */ - struct MLSResult - { - MLSResult () : mean (), plane_normal (), u_axis (), v_axis (), c_vec (), num_neighbors (), curvature (), valid (false) {} - - MLSResult (const Eigen::Vector3d &a_mean, - const Eigen::Vector3d &a_plane_normal, - const Eigen::Vector3d &a_u, - const Eigen::Vector3d &a_v, - const Eigen::VectorXd a_c_vec, - const int a_num_neighbors, - const float &a_curvature); - - Eigen::Vector3d mean, plane_normal, u_axis, v_axis; - Eigen::VectorXd c_vec; - int num_neighbors; - float curvature; - bool valid; - }; + bool cache_mls_results_; /** \brief Stores the MLS result for each point in the input cloud * \note Used only in the case of VOXEL_GRID_DILATION or DISTINCT_CLOUD upsampling */ std::vector mls_results_; - + /** \brief Parameter that specifies the projection method to be used. */ + MLSResult::ProjectionMethod projection_method_; + + /** \brief The maximum number of threads the scheduler should use. */ + unsigned int threads_; + + /** \brief A minimalistic implementation of a voxel grid, necessary for the point cloud upsampling * \note Used only in the case of VOXEL_GRID_DILATION upsampling */ @@ -406,7 +633,7 @@ namespace pcl getCellIndex (const Eigen::Vector3f &p, Eigen::Vector3i& index) const { for (int i = 0; i < 3; ++i) - index[i] = static_cast ((p[i] - bounding_min_(i)) / voxel_size_); + index[i] = static_cast ((p[i] - bounding_min_ (i)) / voxel_size_); } inline void @@ -423,6 +650,7 @@ namespace pcl Eigen::Vector4f bounding_min_, bounding_max_; uint64_t data_size_; float voxel_size_; + EIGEN_MAKE_ALIGNED_OPERATOR_NEW }; @@ -430,7 +658,7 @@ namespace pcl float voxel_size_; /** \brief Number of dilation steps for the VOXEL_GRID_DILATION upsampling method */ - int dilation_iteration_num_; + int dilation_iteration_num_; /** \brief Number of coefficients, to be computed from the requested order.*/ int nr_coeff_; @@ -450,9 +678,8 @@ namespace pcl } /** \brief Smooth a given point and its neighborghood using Moving Least Squares. - * \param[in] index the inex of the query point in the input cloud + * \param[in] index the index of the query point in the input cloud * \param[in] nn_indices the set of nearest neighbors indices for pt - * \param[in] nn_sqr_dists the set of nearest neighbors squared distances for pt * \param[out] projected_points the set of points projected points around the query point * (in the case of upsampling method NONE, only the query point projected to its own fitted surface will be returned, * in the case of the other upsampling methods, multiple points will be returned) @@ -464,50 +691,46 @@ namespace pcl void computeMLSPointNormal (int index, const std::vector &nn_indices, - std::vector &nn_sqr_dists, PointCloudOut &projected_points, NormalCloud &projected_points_normals, PointIndices &corresponding_input_indices, MLSResult &mls_result) const; - /** \brief Fits a point (sample point) given in the local plane coordinates of an input point (query point) to - * the MLS surface of the input point - * \param[in] u_disp the u coordinate of the sample point in the local plane of the query point - * \param[in] v_disp the v coordinate of the sample point in the local plane of the query point - * \param[in] u_axis the axis corresponding to the u-coordinates of the local plane of the query point - * \param[in] v_axis the axis corresponding to the v-coordinates of the local plane of the query point - * \param[in] n_axis - * \param mean - * \param[in] curvature the curvature of the surface at the query point - * \param[in] c_vec the coefficients of the polynomial fit on the MLS surface of the query point - * \param[in] num_neighbors the number of neighbors of the query point in the input cloud - * \param[out] result_point the absolute 3D position of the resulting projected point - * \param[out] result_normal the normal of the resulting projected point + + /** \brief This is a helper function for add projected points + * \param[in] index the index of the query point in the input cloud + * \param[in] point the projected point to be added + * \param[in] normal the projected point's normal to be added + * \param[in] curvature the projected point's curvature + * \param[out] projected_points the set of projected points around the query point + * \param[out] projected_points_normals the normals corresponding to the projected points + * \param[out] corresponding_input_indices the set of indices with each point in output having the corresponding point in input */ void - projectPointToMLSSurface (float &u_disp, float &v_disp, - Eigen::Vector3d &u_axis, Eigen::Vector3d &v_axis, - Eigen::Vector3d &n_axis, - Eigen::Vector3d &mean, - float &curvature, - Eigen::VectorXd &c_vec, - int num_neighbors, - PointOutT &result_point, - pcl::Normal &result_normal) const; + addProjectedPointNormal (int index, + const Eigen::Vector3d &point, + const Eigen::Vector3d &normal, + double curvature, + PointCloudOut &projected_points, + NormalCloud &projected_points_normals, + PointIndices &corresponding_input_indices) const; + void copyMissingFields (const PointInT &point_in, PointOutT &point_out) const; - /** \brief Abstract surface reconstruction method. - * \param[out] output the result of the reconstruction + /** \brief Abstract surface reconstruction method. + * \param[out] output the result of the reconstruction */ - virtual void performProcessing (PointCloudOut &output); + virtual void + performProcessing (PointCloudOut &output); /** \brief Perform upsampling for the distinct-cloud and voxel-grid methods * \param[out] output the result of the reconstruction */ - void performUpsampling (PointCloudOut &output); + void + performUpsampling (PointCloudOut &output); private: /** \brief Boost-based random number generator algorithm. */ @@ -516,76 +739,32 @@ namespace pcl /** \brief Random number generator using an uniform distribution of floats * \note Used only in the case of RANDOM_UNIFORM_DENSITY upsampling */ - boost::shared_ptr > + boost::shared_ptr > > rng_uniform_distribution_; /** \brief Abstract class get name method. */ - std::string getClassName () const { return ("MovingLeastSquares"); } - - public: - EIGEN_MAKE_ALIGNED_OPERATOR_NEW + std::string + getClassName () const { return ("MovingLeastSquares"); } }; -#ifdef _OPENMP - /** \brief MovingLeastSquaresOMP is a parallelized version of MovingLeastSquares, using the OpenMP standard. - * \note Compared to MovingLeastSquares, an overhead is incurred in terms of runtime and memory usage. - * \note The upsampling methods DISTINCT_CLOUD and VOXEL_GRID_DILATION are not parallelized completely, i.e. parts of the algorithm run on a single thread only. - * \author Robert Huitl - * \ingroup surface - */ + /** \brief MovingLeastSquaresOMP implementation has been merged into MovingLeastSquares for better maintainability. + * \note Keeping this empty child class for backwards compatibility. + * \author Robert Huitl + * \ingroup surface + */ template - class MovingLeastSquaresOMP: public MovingLeastSquares + class MovingLeastSquaresOMP : public MovingLeastSquares { public: - typedef boost::shared_ptr > Ptr; - typedef boost::shared_ptr > ConstPtr; - - using PCLBase::input_; - using PCLBase::indices_; - using MovingLeastSquares::normals_; - using MovingLeastSquares::corresponding_input_indices_; - using MovingLeastSquares::nr_coeff_; - using MovingLeastSquares::order_; - using MovingLeastSquares::compute_normals_; - using MovingLeastSquares::upsample_method_; - using MovingLeastSquares::VOXEL_GRID_DILATION; - using MovingLeastSquares::DISTINCT_CLOUD; - - typedef pcl::PointCloud NormalCloud; - typedef pcl::PointCloud::Ptr NormalCloudPtr; - - typedef pcl::PointCloud PointCloudOut; - typedef typename PointCloudOut::Ptr PointCloudOutPtr; - typedef typename PointCloudOut::ConstPtr PointCloudOutConstPtr; - /** \brief Constructor for parallelized Moving Least Squares - * \param threads the maximum number of hardware threads to use (0 sets the value to 1) - */ - MovingLeastSquaresOMP (unsigned int threads = 0) : threads_ (threads) - { - - } - - /** \brief Set the maximum number of threads to use - * \param threads the maximum number of hardware threads to use (0 sets the value to 1) - */ - inline void - setNumberOfThreads (unsigned int threads = 0) + * \param threads the maximum number of hardware threads to use (0 sets the value to 1) + */ + MovingLeastSquaresOMP (unsigned int threads = 1) { - threads_ = threads; + this->setNumberOfThreads (threads); } - - protected: - /** \brief Abstract surface reconstruction method. - * \param[out] output the result of the reconstruction - */ - virtual void performProcessing (PointCloudOut &output); - - /** \brief The maximum number of threads the scheduler should use. */ - unsigned int threads_; }; -#endif } #ifdef PCL_NO_PRECOMPILE diff --git a/surface/include/pcl/surface/poisson.h b/surface/include/pcl/surface/poisson.h index 5b2b1ffa..c090e980 100644 --- a/surface/include/pcl/surface/poisson.h +++ b/surface/include/pcl/surface/poisson.h @@ -138,7 +138,7 @@ namespace pcl inline void setSolverDivide (int solver_divide) { solver_divide_ = solver_divide; } - /** \brief Get the the depth at which a block Gauss-Seidel solver is used to solve the Laplacian equation */ + /** \brief Get the depth at which a block Gauss-Seidel solver is used to solve the Laplacian equation */ inline int getSolverDivide () { return solver_divide_; } diff --git a/surface/src/3rdparty/opennurbs/opennurbs_archive.cpp b/surface/src/3rdparty/opennurbs/opennurbs_archive.cpp index b5574c20..b8fa3754 100644 --- a/surface/src/3rdparty/opennurbs/opennurbs_archive.cpp +++ b/surface/src/3rdparty/opennurbs/opennurbs_archive.cpp @@ -3839,7 +3839,7 @@ bool ON_BinaryArchive::WriteObjectUserData( const ON_Object& object ) { // 22 January 2004 Dale Lear // Disable crc checking when writing the - // unknow user data block. + // unknown user data block. // This has to be done so we don't get an extra // 32 bit CRC calculated on the block that // ON_UnknownUserData::Write() writes. The diff --git a/surface/src/3rdparty/opennurbs/opennurbs_curve.cpp b/surface/src/3rdparty/opennurbs/opennurbs_curve.cpp index 62516064..1003adbd 100644 --- a/surface/src/3rdparty/opennurbs/opennurbs_curve.cpp +++ b/surface/src/3rdparty/opennurbs/opennurbs_curve.cpp @@ -1182,7 +1182,7 @@ bool ON_Curve::EvaluatePoint( const class ON_ObjRef& objref, ON_3dPoint& P ) con ON_3dPoint F1, F2; if ( ellipse.GetFoci(F1,F2) ) { - P = ( F1.DistanceTo(Q) <= F1.DistanceTo(Q)) ? F1 : F2; + P = ( F1.DistanceTo(Q) <= F2.DistanceTo(Q)) ? F1 : F2; rc = true; } } diff --git a/surface/src/mls.cpp b/surface/src/mls.cpp index f7eeead4..e380af5f 100644 --- a/surface/src/mls.cpp +++ b/surface/src/mls.cpp @@ -37,18 +37,17 @@ * */ -#include -#include #include #include -// Instantiations of specific point types -PCL_INSTANTIATE_PRODUCT(MovingLeastSquares, ((pcl::PointXYZ)(pcl::PointXYZRGB)(pcl::PointXYZRGBA)(pcl::PointXYZRGBNormal)(pcl::PointNormal)) - ((pcl::PointXYZ)(pcl::PointXYZRGB)(pcl::PointXYZRGBA)(pcl::PointXYZRGBNormal)(pcl::PointNormal))) - -#ifdef _OPENMP -PCL_INSTANTIATE_PRODUCT(MovingLeastSquaresOMP, ((pcl::PointXYZ)(pcl::PointXYZRGB)(pcl::PointXYZRGBA)(pcl::PointXYZRGBNormal)(pcl::PointNormal)) - ((pcl::PointXYZ)(pcl::PointXYZRGB)(pcl::PointXYZRGBA)(pcl::PointXYZRGBNormal)(pcl::PointNormal))) +#ifndef PCL_NO_PRECOMPILE +#include +#include +#ifdef PCL_ONLY_CORE_POINT_TYPES + // Instantiations of specific point types + PCL_INSTANTIATE_PRODUCT(MovingLeastSquares, ((pcl::PointXYZ)(pcl::PointXYZI)(pcl::PointXYZRGB)(pcl::PointXYZRGBA)(pcl::PointXYZRGBNormal)(pcl::PointNormal)) + ((pcl::PointXYZ)(pcl::PointXYZI)(pcl::PointXYZRGB)(pcl::PointXYZRGBA)(pcl::PointXYZRGBNormal)(pcl::PointNormal))) +#else + PCL_INSTANTIATE_PRODUCT(MovingLeastSquares, (PCL_XYZ_POINT_TYPES)(PCL_XYZ_POINT_TYPES)) #endif -/// Ideally, we should instantiate like below, but it takes large amounts of main memory for compilation -//PCL_INSTANTIATE_PRODUCT(MovingLeastSquares, (PCL_XYZ_POINT_TYPES)(PCL_XYZ_POINT_TYPES)) +#endif // PCL_NO_PRECOMPILE diff --git a/surface/src/on_nurbs/sequential_fitter.cpp b/surface/src/on_nurbs/sequential_fitter.cpp index e88d1eca..2a5677d3 100644 --- a/surface/src/on_nurbs/sequential_fitter.cpp +++ b/surface/src/on_nurbs/sequential_fitter.cpp @@ -235,10 +235,10 @@ SequentialFitter::setCorners (pcl::PointIndices::Ptr &corners, bool flip_on_dema throw std::runtime_error ("[SequentialFitter::setCorners] Error: Empty or invalid pcl-point-cloud.\n"); if (corners->indices.size () < 4) - throw std::runtime_error ("[SequentialFitter::setCorners] Error: to few corners (<4)\n"); + throw std::runtime_error ("[SequentialFitter::setCorners] Error: too few corners (<4)\n"); if (corners->indices.size () > 4) - printf ("[SequentialFitter::setCorners] Warning: to many corners (>4)\n"); + printf ("[SequentialFitter::setCorners] Warning: too many corners (>4)\n"); bool flip = false; pcl::PointXYZRGB &pt0 = m_cloud->at (corners->indices[0]); diff --git a/test/bun0.pcd b/test/bun0.pcd index f35c8355..00d1886b 100644 --- a/test/bun0.pcd +++ b/test/bun0.pcd @@ -1,407 +1,408 @@ -# .PCD v.5 - Point Cloud Data file format -VERSION .5 -FIELDS x y z -SIZE 4 4 4 -TYPE F F F -COUNT 1 1 1 +# .PCD v0.7 - Point Cloud Data file format +VERSION 0.7 +FIELDS x y z normal_x normal_y normal_z curvature +SIZE 4 4 4 4 4 4 4 +TYPE F F F F F F F +COUNT 1 1 1 1 1 1 1 WIDTH 397 HEIGHT 1 +VIEWPOINT 0 0 0 1 0 0 0 POINTS 397 DATA ascii -0.0054216 0.11349 0.040749 --0.0017447 0.11425 0.041273 --0.010661 0.11338 0.040916 -0.026422 0.11499 0.032623 -0.024545 0.12284 0.024255 -0.034137 0.11316 0.02507 -0.02886 0.11773 0.027037 -0.02675 0.12234 0.017605 -0.03575 0.1123 0.019109 -0.015982 0.12307 0.031279 -0.0079813 0.12438 0.032798 -0.018101 0.11674 0.035493 -0.0086687 0.11758 0.037538 -0.01808 0.12536 0.026132 -0.0080861 0.12866 0.02619 -0.02275 0.12146 0.029671 --0.0018689 0.12456 0.033184 --0.011168 0.12376 0.032519 --0.0020063 0.11937 0.038104 --0.01232 0.11816 0.037427 --0.0016659 0.12879 0.026782 --0.011971 0.12723 0.026219 -0.016484 0.12828 0.01928 -0.0070921 0.13103 0.018415 -0.0014615 0.13134 0.017095 --0.013821 0.12886 0.019265 --0.01725 0.11202 0.040077 --0.074556 0.13415 0.051046 --0.065971 0.14396 0.04109 --0.071925 0.14545 0.043266 --0.06551 0.13624 0.042195 --0.071112 0.13767 0.047518 --0.079528 0.13416 0.051194 --0.080421 0.14428 0.042793 --0.082672 0.1378 0.046806 --0.08813 0.13514 0.042222 --0.066325 0.12347 0.050729 --0.072399 0.12662 0.052364 --0.066091 0.11973 0.050881 --0.072012 0.11811 0.052295 --0.062433 0.12627 0.043831 --0.068326 0.12998 0.048875 --0.063094 0.11811 0.044399 --0.071301 0.11322 0.04841 --0.080515 0.12741 0.052034 --0.078179 0.1191 0.051116 --0.085216 0.12609 0.049001 --0.089538 0.12621 0.044589 --0.082659 0.11661 0.04797 --0.089536 0.11784 0.04457 --0.0565 0.15248 0.030132 --0.055517 0.15313 0.026915 --0.03625 0.17198 0.00017688 --0.03775 0.17198 0.00022189 --0.03625 0.16935 0.00051958 --0.033176 0.15711 0.0018682 --0.051913 0.1545 0.011273 --0.041707 0.16642 0.0030522 --0.049468 0.16414 0.0041988 --0.041892 0.15669 0.0054879 --0.051224 0.15878 0.0080283 --0.062417 0.15317 0.033161 --0.07167 0.15319 0.033701 --0.062543 0.15524 0.027405 --0.07211 0.1555 0.027645 --0.078663 0.15269 0.032268 --0.081569 0.15374 0.026085 --0.08725 0.1523 0.022135 --0.05725 0.15568 0.010325 --0.057888 0.1575 0.0073225 --0.0885 0.15223 0.019215 --0.056129 0.14616 0.03085 --0.054705 0.13555 0.032127 --0.054144 0.14714 0.026275 --0.046625 0.13234 0.021909 --0.05139 0.13694 0.025787 --0.018278 0.12238 0.030773 --0.021656 0.11643 0.035209 --0.031921 0.11566 0.032851 --0.021348 0.12421 0.024562 --0.03241 0.12349 0.023293 --0.024869 0.12094 0.028745 --0.031747 0.12039 0.028229 --0.052912 0.12686 0.034968 --0.041672 0.11564 0.032998 --0.052037 0.1168 0.034582 --0.042495 0.12488 0.024082 --0.047946 0.12736 0.028108 --0.042421 0.12035 0.028633 --0.047661 0.12024 0.028871 --0.035964 0.1513 0.0005395 --0.050598 0.1474 0.013881 --0.046375 0.13293 0.018289 --0.049125 0.13856 0.016269 --0.042976 0.14915 0.0054003 --0.047965 0.14659 0.0086783 --0.022926 0.1263 0.018077 --0.031583 0.1259 0.017804 --0.041733 0.12796 0.01665 --0.061482 0.14698 0.036168 --0.071729 0.15026 0.038328 --0.060526 0.1368 0.035999 --0.082619 0.14823 0.035955 --0.087824 0.14449 0.033779 --0.089 0.13828 0.037774 --0.085662 0.15095 0.028208 --0.089601 0.14725 0.025869 --0.090681 0.13748 0.02369 --0.058722 0.12924 0.038992 --0.060075 0.11512 0.037685 --0.091812 0.12767 0.038703 --0.091727 0.11657 0.039619 --0.093164 0.12721 0.025211 --0.093938 0.12067 0.024399 --0.091583 0.14522 0.01986 --0.090929 0.13667 0.019817 --0.093094 0.11635 0.018959 -0.024948 0.10286 0.041418 -0.0336 0.092627 0.040463 -0.02742 0.096386 0.043312 -0.03392 0.086911 0.041034 -0.028156 0.086837 0.045084 -0.03381 0.078604 0.040854 -0.028125 0.076874 0.045059 -0.0145 0.093279 0.05088 -0.0074817 0.09473 0.052315 -0.017407 0.10535 0.043139 -0.0079536 0.10633 0.042968 -0.018511 0.097194 0.047253 -0.0086436 0.099323 0.048079 --0.0020197 0.095698 0.053906 --0.011446 0.095169 0.053862 --0.001875 0.10691 0.043455 --0.011875 0.10688 0.043019 --0.0017622 0.10071 0.046648 --0.012498 0.10008 0.045916 -0.016381 0.085894 0.051642 -0.0081167 0.08691 0.055228 -0.017644 0.076955 0.052372 -0.008125 0.076853 0.055536 -0.020575 0.088169 0.049006 -0.022445 0.075721 0.049563 --0.0017931 0.086849 0.056843 --0.011943 0.086771 0.057009 --0.0019567 0.076863 0.057803 --0.011875 0.076964 0.057022 -0.03325 0.067541 0.040033 -0.028149 0.066829 0.042953 -0.026761 0.057829 0.042588 -0.023571 0.04746 0.040428 -0.015832 0.067418 0.051639 -0.0080431 0.066902 0.055006 -0.013984 0.058886 0.050416 -0.0080973 0.056888 0.05295 -0.020566 0.065958 0.0483 -0.018594 0.056539 0.047879 -0.012875 0.052652 0.049689 --0.0017852 0.066712 0.056503 --0.011785 0.066885 0.055015 --0.001875 0.056597 0.05441 --0.01184 0.057054 0.052714 --0.015688 0.052469 0.049615 -0.0066154 0.04993 0.051259 -0.018088 0.046655 0.043321 -0.008841 0.045437 0.046623 -0.017688 0.039719 0.043084 -0.008125 0.039516 0.045374 --0.0016111 0.049844 0.05172 --0.01245 0.046773 0.050903 --0.013851 0.039778 0.051036 --0.0020294 0.044874 0.047587 --0.011653 0.04686 0.048661 --0.0018611 0.039606 0.047339 --0.0091545 0.03958 0.049415 -0.043661 0.094028 0.02252 -0.034642 0.10473 0.031831 -0.028343 0.1072 0.036339 -0.036339 0.096552 0.034843 -0.031733 0.099372 0.038505 -0.036998 0.10668 0.026781 -0.032875 0.11108 0.02959 -0.040938 0.097132 0.026663 -0.044153 0.086466 0.024241 -0.05375 0.072221 0.020429 -0.04516 0.076574 0.023594 -0.038036 0.086663 0.035459 -0.037861 0.076625 0.035658 -0.042216 0.087237 0.028254 -0.042355 0.076747 0.02858 -0.043875 0.096228 0.015269 -0.044375 0.096797 0.0086445 -0.039545 0.1061 0.017655 -0.042313 0.10009 0.017237 -0.045406 0.087417 0.015604 -0.055118 0.072639 0.017944 -0.048722 0.07376 0.017434 -0.045917 0.086298 0.0094211 -0.019433 0.1096 0.039063 -0.01097 0.11058 0.039648 -0.046657 0.057153 0.031337 -0.056079 0.066335 0.024122 -0.048168 0.06701 0.026298 -0.056055 0.057253 0.024902 -0.051163 0.056662 0.029137 -0.036914 0.067032 0.036122 -0.033 0.06472 0.039903 -0.038004 0.056507 0.033119 -0.030629 0.054915 0.038484 -0.041875 0.066383 0.028357 -0.041434 0.06088 0.029632 -0.044921 0.049904 0.031243 -0.054635 0.050167 0.022044 -0.04828 0.04737 0.025845 -0.037973 0.048347 0.031456 -0.028053 0.047061 0.035991 -0.025595 0.040346 0.03415 -0.038455 0.043509 0.028278 -0.032031 0.043278 0.029253 -0.036581 0.040335 0.025144 -0.03019 0.039321 0.026847 -0.059333 0.067891 0.017361 -0.0465 0.071452 0.01971 -0.059562 0.057747 0.01834 -0.055636 0.049199 0.019173 -0.0505 0.045064 0.019181 -0.023 0.047803 0.039776 -0.022389 0.03886 0.038795 --0.019545 0.0939 0.052205 --0.021462 0.10618 0.042059 --0.031027 0.10395 0.041228 --0.022521 0.097723 0.045194 --0.031858 0.097026 0.043878 --0.043262 0.10412 0.040891 --0.052154 0.10404 0.040972 --0.041875 0.096944 0.042424 --0.051919 0.096967 0.043563 --0.021489 0.086672 0.054767 --0.027 0.083087 0.050284 --0.02107 0.077249 0.054365 --0.026011 0.089634 0.048981 --0.031893 0.087035 0.044169 --0.025625 0.074892 0.047102 --0.03197 0.0769 0.042177 --0.041824 0.086954 0.043295 --0.051825 0.086844 0.044933 --0.041918 0.076728 0.042564 --0.051849 0.076877 0.042992 --0.061339 0.10393 0.041164 --0.072672 0.10976 0.044294 --0.061784 0.096825 0.043327 --0.070058 0.096203 0.041397 --0.080439 0.11091 0.044343 --0.061927 0.086724 0.04452 --0.070344 0.087352 0.041908 --0.06141 0.077489 0.042178 --0.068579 0.080144 0.041024 --0.019045 0.067732 0.052388 --0.017742 0.058909 0.050809 --0.023548 0.066382 0.045226 --0.03399 0.067795 0.040929 --0.02169 0.056549 0.045164 --0.036111 0.060706 0.040407 --0.041231 0.066951 0.041392 --0.048588 0.070956 0.040357 --0.0403 0.059465 0.040446 --0.02192 0.044965 0.052258 --0.029187 0.043585 0.051088 --0.021919 0.039826 0.053521 --0.030331 0.039749 0.052133 --0.021998 0.049847 0.046725 --0.031911 0.046848 0.045187 --0.035276 0.039753 0.047529 --0.042016 0.044823 0.041594 --0.05194 0.044707 0.043498 --0.041928 0.039327 0.043582 --0.051857 0.039252 0.046212 --0.059453 0.04424 0.042862 --0.060765 0.039087 0.044363 --0.024273 0.11038 0.039129 --0.032379 0.10878 0.037952 --0.041152 0.10853 0.037969 --0.051698 0.10906 0.038258 --0.062091 0.10877 0.038274 --0.071655 0.10596 0.037516 --0.074634 0.097746 0.038347 --0.07912 0.10508 0.032308 --0.080203 0.096758 0.033592 --0.08378 0.10568 0.025985 --0.087292 0.10314 0.020825 --0.08521 0.097079 0.02781 --0.088082 0.096456 0.022985 --0.07516 0.08604 0.038816 --0.064577 0.073455 0.03897 --0.072279 0.076416 0.036413 --0.076375 0.072563 0.02873 --0.080031 0.087076 0.03429 --0.078919 0.079371 0.032477 --0.084834 0.086686 0.026974 --0.087891 0.089233 0.022611 --0.081048 0.077169 0.025829 --0.086393 0.10784 0.018635 --0.087672 0.10492 0.017264 --0.089333 0.098483 0.01761 --0.086375 0.083067 0.018607 --0.089179 0.089186 0.018947 --0.082879 0.076109 0.017794 --0.0825 0.074674 0.0071175 --0.026437 0.064141 0.039321 --0.030035 0.06613 0.038942 --0.026131 0.056531 0.038882 --0.031664 0.056657 0.037742 --0.045716 0.064541 0.039166 --0.051959 0.066869 0.036733 --0.042557 0.055545 0.039026 --0.049406 0.056892 0.034344 --0.0555 0.062391 0.029498 --0.05375 0.058574 0.026313 --0.03406 0.050137 0.038577 --0.041741 0.04959 0.03929 --0.050975 0.049435 0.036965 --0.053 0.051065 0.029209 --0.054145 0.054568 0.012257 --0.055848 0.05417 0.0083272 --0.054844 0.049295 0.011462 --0.05615 0.050619 0.0092929 --0.061451 0.068257 0.035376 --0.069725 0.069958 0.032788 --0.062823 0.063322 0.026886 --0.071037 0.066787 0.025228 --0.060857 0.060568 0.022643 --0.067 0.061558 0.020109 --0.0782 0.071279 0.021032 --0.062116 0.045145 0.037802 --0.065473 0.039513 0.037964 --0.06725 0.03742 0.033413 --0.072702 0.065008 0.018701 --0.06145 0.059165 0.018731 --0.0675 0.061479 0.019221 --0.057411 0.054114 0.0038257 --0.079222 0.070654 0.017735 --0.062473 0.04468 0.01111 --0.06725 0.042258 0.010414 --0.066389 0.040515 0.01316 --0.068359 0.038502 0.011958 --0.061381 0.04748 0.007607 --0.068559 0.043549 0.0081576 --0.070929 0.03983 0.0085888 --0.016625 0.18375 -0.019735 --0.015198 0.17471 -0.018868 --0.015944 0.16264 -0.0091037 --0.015977 0.1607 -0.0088072 --0.013251 0.16708 -0.015264 --0.014292 0.16098 -0.011252 --0.013986 0.184 -0.023739 --0.011633 0.17699 -0.023349 --0.0091029 0.16988 -0.021457 --0.025562 0.18273 -0.0096247 --0.02725 0.18254 -0.0094384 --0.025736 0.17948 -0.0089653 --0.031216 0.17589 -0.0051154 --0.020399 0.1845 -0.014943 --0.021339 0.17645 -0.014566 --0.027125 0.17234 -0.010156 --0.03939 0.1733 -0.0023575 --0.022876 0.16406 -0.0078103 --0.031597 0.16651 -0.0049292 --0.0226 0.15912 -0.003799 --0.030372 0.15767 -0.0012672 --0.021158 0.16849 -0.012383 --0.027 0.1712 -0.01022 --0.041719 0.16813 -0.00074958 --0.04825 0.16748 -0.00015191 --0.03725 0.16147 -7.2628e-05 --0.066429 0.15783 -0.0085673 --0.071284 0.15839 -0.005998 --0.065979 0.16288 -0.017792 --0.071623 0.16384 -0.01576 --0.066068 0.16051 -0.013567 --0.073307 0.16049 -0.011832 --0.077 0.16204 -0.019241 --0.077179 0.15851 -0.01495 --0.073691 0.17286 -0.037944 --0.07755 0.17221 -0.039175 --0.065921 0.16586 -0.025022 --0.072095 0.16784 -0.024725 --0.066 0.16808 -0.030916 --0.073448 0.17051 -0.032045 --0.07777 0.16434 -0.025938 --0.077893 0.16039 -0.021299 --0.078211 0.169 -0.034566 --0.034667 0.15131 -0.00071029 --0.066117 0.17353 -0.047453 --0.071986 0.17612 -0.045384 --0.06925 0.182 -0.055026 --0.064992 0.17802 -0.054645 --0.069935 0.17983 -0.051988 --0.07793 0.17516 -0.0444 +0.0054215998 0.11349 0.040748999 -0.16884723 -0.45159745 -0.87609947 0.0030943851 +-0.0017447 0.11425 0.041273002 -0.011541721 -0.46529692 -0.88507944 0.0056861709 +-0.010661 0.11338 0.040916 0.14614862 -0.496488 -0.85565197 0.0056441575 +0.026422 0.11499 0.032623 -0.52052444 -0.53684396 -0.66396749 0.0069778459 +0.024545001 0.12284 0.024255 -0.50764346 -0.74296999 -0.4362267 0.012873111 +0.034136999 0.11316 0.02507 -0.74675208 -0.59207439 -0.30300051 0.00816618 +0.028860001 0.11773 0.027037 -0.59785169 -0.67759848 -0.42829144 0.015595004 +0.02675 0.12234 0.017604999 -0.59158164 -0.74482262 -0.30865923 0.0096910615 +0.035750002 0.1123 0.019109 -0.79553264 -0.56393856 -0.22158764 0.0077297003 +0.015982 0.12307 0.031279001 -0.31086302 -0.74870217 -0.58549905 0.010855215 +0.0079813004 0.12438 0.032798 -0.15485749 -0.70128393 -0.69585913 0.0058102743 +0.018100999 0.11674 0.035493001 -0.32298067 -0.56194073 -0.76151562 0.0089428844 +0.0086687002 0.11758 0.037537999 -0.17445859 -0.523031 -0.83426785 0.0039783465 +0.01808 0.12536 0.026132001 -0.40127173 -0.79735625 -0.45078158 0.011859259 +0.0080861002 0.12865999 0.02619 -0.14846934 -0.86061466 -0.48713383 0.01193497 +0.02275 0.12146 0.029671 -0.50815374 -0.73633546 -0.44675478 0.013027777 +-0.0018689 0.12456 0.033183999 0.021843739 -0.73292792 -0.6799556 0.0062952945 +-0.011168 0.12376 0.032519002 0.21974316 -0.7375769 -0.63850862 0.0058935038 +-0.0020063 0.11937 0.038104001 0.0033577818 -0.55838519 -0.82957506 0.0055112438 +-0.01232 0.11816 0.037427001 0.18408856 -0.58010685 -0.79346544 0.0045971852 +-0.0016659 0.12879001 0.026782 0.060926694 -0.89214551 -0.44762078 0.0071681938 +-0.011971 0.12723 0.026218999 0.25322157 -0.84287763 -0.474801 0.004851752 +0.016484 0.12828 0.01928 -0.30896688 -0.90670973 -0.28708377 0.0081693754 +0.0070921001 0.13102999 0.018415 -0.18857701 -0.8954969 -0.40314254 0.0061773811 +0.0014615 0.13134 0.017095 -0.030506011 -0.93418866 -0.35547286 0.0078295255 +-0.013821 0.12886 0.019265 0.20082667 -0.90866685 -0.36605063 0.0041274121 +-0.01725 0.11202 0.040077001 0.17915623 -0.52085388 -0.83463424 0.0074276463 +-0.074556001 0.13415 0.051045999 -0.13361748 -0.37986907 -0.91533923 0.053038858 +-0.065971002 0.14396 0.04109 -0.52148068 -0.439255 -0.73151416 0.026551403 +-0.071924999 0.14545 0.043265998 -0.10976201 -0.60856271 -0.78587765 0.030716548 +-0.065509997 0.13624001 0.042195 -0.64690953 -0.27790511 -0.71012449 0.0061066193 +-0.071111999 0.13767 0.047518 -0.23028101 -0.50395799 -0.8324644 0.024193831 +-0.079527996 0.13416 0.051194001 0.20600085 -0.40636119 -0.89018774 0.042999495 +-0.080421001 0.14428 0.042792998 0.39670554 -0.63132799 -0.66637051 0.020071341 +-0.082672 0.13779999 0.046806 0.49222195 -0.44529268 -0.74795187 0.027716169 +-0.088129997 0.13514 0.042222001 0.71588868 -0.35794571 -0.59948176 0.0061506582 +-0.066325001 0.12347 0.050728999 -0.44702551 0.026569208 -0.89412653 0.049122065 +-0.072398998 0.12661999 0.052363999 -0.15109582 -0.14565089 -0.97772992 0.012987013 +-0.066091001 0.11973 0.050880998 0.49363372 -0.27387455 0.82542014 0.063380361 +-0.072012 0.11811 0.052294999 0.089953512 -0.40322584 0.91066861 0.043889515 +-0.062433001 0.12627 0.043830998 0.7461043 0.10399755 0.65765715 0.025209237 +-0.068325996 0.12998 0.048875 -0.43632671 -0.33852753 -0.83367741 0.025434997 +-0.063093998 0.11811 0.044399001 0.73735356 -0.31851915 0.59569734 0.030531738 +-0.071300998 0.11322 0.048409998 0.16830848 -0.65331757 0.7381385 0.032480057 +-0.080514997 0.12740999 0.052034002 0.38682958 -0.01568961 -0.92201769 0.017833892 +-0.078179002 0.1191 0.051116001 0.15518065 0.31381333 -0.93671775 0.021380307 +-0.085216001 0.12609001 0.049001001 0.66413164 -0.060075819 -0.74519807 0.016970368 +-0.089538001 0.12621 0.044589002 0.75927979 -0.1181355 -0.63995177 0.015361789 +-0.082658999 0.11661 0.047970001 0.4467158 0.38261694 -0.80873305 0.015345791 +-0.089535996 0.11784 0.044569999 0.67966002 0.12636337 -0.72256106 0.032478429 +-0.056499999 0.15248001 0.030131999 -0.33762839 -0.71236515 -0.61525851 0.0429281 +-0.055516999 0.15312999 0.026915001 -0.83237761 -0.38691509 -0.39679241 0.023426346 +-0.036249999 0.17197999 0.00017688 -0.29537779 -0.14131801 -0.94487101 0.06837023 +-0.037749998 0.17197999 0.00022189 -0.29537809 -0.14131688 -0.94487107 0.068370357 +-0.036249999 0.16935 0.00051957997 -0.29537809 -0.14131688 -0.94487107 0.068370357 +-0.033176001 0.15711001 0.0018682 -0.43437883 -0.10727591 -0.89431924 0.027991556 +-0.051913001 0.15449999 0.011273 -0.66571194 -0.53041917 -0.52486485 0.10940588 +-0.041707002 0.16642 0.0030522 -0.088684477 -0.32659593 -0.9409942 0.04067177 +-0.049467999 0.16414 0.0041987998 -0.19820534 -0.51164192 -0.83602464 0.018989481 +-0.041892 0.15669 0.0054879002 -0.46768439 -0.15497798 -0.8702029 0.021614425 +-0.051224001 0.15877999 0.0080282995 -0.36611214 -0.40681309 -0.83693784 0.031957328 +-0.062417001 0.15317 0.033160999 -0.34849492 -0.73624218 -0.58008516 0.044005789 +-0.071670003 0.15319 0.033700999 0.037774783 -0.84826654 -0.52822059 0.019414406 +-0.062542997 0.15524 0.027404999 -0.34841642 -0.73644722 -0.57987207 0.043948863 +-0.072109997 0.15549999 0.027644999 0.084375083 -0.85251749 -0.51584369 0.023248179 +-0.078662999 0.15268999 0.032267999 0.36376482 -0.81080192 -0.4585579 0.0071089235 +-0.081569001 0.15374 0.026085 0.41848749 -0.81323707 -0.40436816 0.011017915 +-0.087250002 0.1523 0.022135001 0.79803133 -0.46404663 -0.38445634 0.034933198 +-0.057250001 0.15568 0.010325 -0.66573882 -0.53037977 -0.52487051 0.10940783 +-0.057888001 0.1575 0.0073225 -0.36614218 -0.40679416 -0.83693397 0.031956732 +-0.088500001 0.15222999 0.019215001 0.8514818 -0.36151975 -0.37984514 0.047997151 +-0.056129001 0.14616001 0.030850001 -0.71406651 -0.25552902 -0.65177757 0.026650187 +-0.054705001 0.13555001 0.032127 -0.75563037 -0.25465611 -0.6034674 0.0062280181 +-0.054143999 0.14714 0.026275 -0.83619982 -0.33200502 -0.43651178 0.027516434 +-0.046624999 0.13234 0.021909 -0.77167147 -0.45983508 -0.43940294 0.015568241 +-0.05139 0.13694 0.025787 -0.82502484 -0.29802176 -0.48012191 0.016481433 +-0.018278001 0.12238 0.030773001 0.2189856 -0.77467448 -0.59323251 0.011681316 +-0.021655999 0.11643 0.035209 0.25612333 -0.60931838 -0.75042123 0.0029822814 +-0.031920999 0.11566 0.032850999 0.089117572 -0.69171697 -0.71664894 0.007182973 +-0.021348 0.12421 0.024561999 0.21852617 -0.88355941 -0.41420898 0.0038163862 +-0.03241 0.12349 0.023293 -0.0098979417 -0.83467448 -0.55065459 0.0082057444 +-0.024869001 0.12094 0.028744999 0.23928009 -0.82670623 -0.50921685 0.0083035426 +-0.031746998 0.12039 0.028229 0.033354413 -0.81189781 -0.58284593 0.0085460329 +-0.052912001 0.12685999 0.034968 -0.6821875 -0.30016235 -0.6667254 0.010716889 +-0.041671999 0.11564 0.032997999 -0.055118669 -0.64711553 -0.7603969 0.0040871259 +-0.052037001 0.1168 0.034582 -0.21478797 -0.3984302 -0.89169478 0.035277426 +-0.042495001 0.12488 0.024081999 -0.28772661 -0.71870273 -0.63299263 0.036527757 +-0.047945999 0.12736 0.028108001 -0.72714043 -0.35215625 -0.58928162 0.024528533 +-0.042420998 0.12035 0.028633 -0.28699586 -0.60819656 -0.74008805 0.046943106 +-0.047660999 0.12024 0.028871 -0.63170999 -0.40763944 -0.65937287 0.035298273 +-0.035964001 0.1513 0.00053949998 0.42597631 0.069760799 0.90204078 0.013660887 +-0.050597999 0.14740001 0.013881 -0.79792613 -0.50649345 -0.3267695 0.056300875 +-0.046374999 0.13293 0.018289 -0.64400738 -0.55972904 -0.52149576 0.027122065 +-0.049125001 0.13856 0.016269 -0.82856554 -0.35174406 -0.4356091 0.011342637 +-0.042975999 0.14915 0.0054003 -0.53052694 -0.1433237 -0.83546358 0.019582529 +-0.047965001 0.14658999 0.0086783003 -0.43282154 -0.42147359 -0.79688495 0.028641256 +-0.022926001 0.12630001 0.018076999 0.21852911 -0.8835566 -0.41421345 0.0038136169 +-0.031583 0.1259 0.017804001 -0.084133193 -0.80922121 -0.58144879 0.018533347 +-0.041733 0.12796 0.016650001 -0.36518443 -0.63293225 -0.68266904 0.027313044 +-0.061482001 0.14698 0.036168002 -0.50408536 -0.44549564 -0.73988628 0.039870977 +-0.071728997 0.15026 0.038327999 -0.015496265 -0.7730304 -0.63417977 0.01780135 +-0.060525998 0.13680001 0.035999 -0.71306747 -0.24283816 -0.65769631 0.0063271122 +-0.082618997 0.14823 0.035955001 0.47669819 -0.71192718 -0.51567286 0.013532816 +-0.087824002 0.14449 0.033778999 0.78517169 -0.52193189 -0.33330569 0.024797359 +-0.089000002 0.13828 0.037774 0.84258002 -0.39418849 -0.36697993 0.014366952 +-0.085662 0.15095 0.028208001 0.5251044 -0.78348589 -0.33228782 0.025516272 +-0.089601003 0.14725 0.025869001 0.89614761 -0.32351896 -0.30373508 0.034004856 +-0.090681002 0.13748001 0.02369 0.95984846 -0.22861007 -0.16256762 0.007631606 +-0.058722001 0.12924001 0.038991999 -0.70580089 -0.12846732 -0.69666433 0.016850151 +-0.060075 0.11512 0.037684999 -0.49193445 -0.058857333 -0.86864048 0.040929805 +-0.091812 0.12767 0.038702998 0.93956327 -0.17970741 -0.2914207 0.014043191 +-0.091727003 0.11657 0.039618999 0.86413795 0.33086988 -0.37919742 0.048940618 +-0.093163997 0.12721001 0.025211001 0.98552257 -0.12056172 -0.11920663 0.0043064994 +-0.093938001 0.12067 0.024398999 0.98427206 0.16453789 0.064309932 0.029838379 +-0.091582999 0.14522 0.019859999 0.89614254 -0.32352197 -0.30374691 0.034012903 +-0.090929002 0.13666999 0.019817 0.95805603 -0.20726846 -0.1979098 0.0057622446 +-0.093093999 0.11635 0.018959001 0.98243999 0.18117557 0.044576686 0.032072492 +0.024948001 0.10286 0.041418001 -0.50217587 -0.45149371 -0.73754513 0.0049769632 +0.033599999 0.092626996 0.040463001 -0.66844445 -0.23813689 -0.70460826 0.0084011387 +0.027419999 0.096386001 0.043311998 -0.52449149 -0.31101868 -0.79257548 0.0042440598 +0.033920001 0.086911 0.041034002 -0.68985844 -0.097785927 -0.71730965 0.0065412661 +0.028155999 0.086837001 0.045084 -0.5732041 -0.084137343 -0.81508148 0.0041391035 +0.033810001 0.078603998 0.040854 -0.6749531 0.049827036 -0.73617643 0.0027250601 +0.028124999 0.076874003 0.045058999 -0.61608005 0.092481732 -0.78223556 0.0023465508 +0.0145 0.093278997 0.05088 -0.37729472 -0.35353231 -0.85595769 0.0052667796 +0.0074816998 0.094729997 0.052315 -0.18586323 -0.50761348 -0.8412987 0.012646539 +0.017407 0.10535 0.043138999 -0.24998525 -0.50104308 -0.82853079 0.0050569442 +0.0079536 0.10633 0.042968001 -0.12115002 -0.45767561 -0.88082665 0.005781922 +0.018510999 0.097194001 0.047253001 -0.37112617 -0.34566584 -0.8618471 0.0054108021 +0.0086436002 0.099322997 0.048078999 -0.11644173 -0.59730524 -0.7935161 0.0055130087 +-0.0020196999 0.095697999 0.053906001 -0.024833232 -0.57589561 -0.81714606 0.012671551 +-0.011446 0.095169 0.053862002 0.13523138 -0.59376568 -0.7931928 0.014543219 +-0.001875 0.10691 0.043455001 0.026278881 -0.39993814 -0.91616535 0.002705948 +-0.011875 0.10688 0.043019 0.1192934 -0.39521939 -0.91080779 0.0015582 +-0.0017622 0.10071 0.046647999 -0.0069597415 -0.58737713 -0.80928338 0.015852297 +-0.012498 0.10008 0.045915999 0.11381002 -0.60800987 -0.78572983 0.015490185 +0.016380999 0.085894004 0.051642001 -0.40546137 -0.17009516 -0.89814734 0.0030322804 +0.0081166998 0.086910002 0.055227999 -0.29560381 -0.25577238 -0.92043406 0.0076185586 +0.017643999 0.076954998 0.052372001 -0.44164523 0.016793245 -0.89703256 0.0038809509 +0.0081249997 0.076853 0.055535998 -0.28453448 0.014439251 -0.95855707 0.0030511376 +0.020575 0.088169001 0.049006 -0.48251823 -0.14953297 -0.86302727 0.0021532676 +0.022445001 0.075721003 0.049563002 -0.54274106 0.042393196 -0.83882946 0.0043320926 +-0.0017931 0.086848997 0.056843001 -0.085129447 -0.29672778 -0.95116013 0.021261316 +-0.011943 0.086770996 0.057009 0.19515742 -0.19225521 -0.96174401 0.012458436 +-0.0019567001 0.076862998 0.057803001 -0.055512048 -0.015349915 -0.99833995 0.007448623 +-0.011875 0.076963998 0.057022002 0.2755622 0.10759915 -0.95524234 0.020187844 +0.03325 0.067541003 0.040033001 -0.64387792 0.13396259 -0.75330955 0.0024370786 +0.028148999 0.066829003 0.042952999 -0.59509897 0.15709978 -0.78814769 0.001263492 +0.026760999 0.057829 0.042587999 -0.56684518 0.27811375 -0.77546078 0.0032190143 +0.023571 0.047460001 0.040428001 -0.60811114 0.31426507 -0.72899812 0.0063965297 +0.015831999 0.067418002 0.051639002 -0.46439239 0.15635081 -0.87171906 0.0033340643 +0.0080431001 0.066901997 0.055006001 -0.29502016 0.15021715 -0.943609 0.0037481887 +0.013984 0.058885999 0.050416 -0.44073677 0.28497621 -0.85119891 0.0053655636 +0.0080973003 0.056887999 0.052949999 -0.29052511 0.28976738 -0.91193748 0.0064742533 +0.020566 0.065958001 0.048300002 -0.51491582 0.17084281 -0.84004426 0.0017491818 +0.018594 0.056538999 0.047878999 -0.51175618 0.28225034 -0.81144345 0.0029353874 +0.012875 0.052652001 0.049688999 -0.38840196 0.3981083 -0.83105576 0.0085874954 +-0.0017852 0.066711999 0.056503002 -0.020316985 0.17890212 -0.98365712 0.004140513 +-0.011785 0.066885002 0.055015001 0.32926905 0.21563224 -0.91928482 0.01968986 +-0.001875 0.056597002 0.054409999 -0.0052823606 0.27603641 -0.96113271 0.0068042353 +-0.01184 0.057054002 0.052714001 0.32840732 0.26231068 -0.90738177 0.016386257 +-0.015688 0.052469 0.049614999 0.24083789 0.01177044 -0.97049403 0.059673704 +0.0066153998 0.049929999 0.051259 -0.19795869 0.38478413 -0.90152842 0.0084427586 +0.018088 0.046654999 0.043320999 -0.48477679 0.32723331 -0.8111164 0.012247019 +0.0088409996 0.045437001 0.046622999 -0.21786378 0.42153558 -0.88025171 0.010199225 +0.017688001 0.039719 0.043083999 -0.50713331 0.31902084 -0.80065072 0.014214325 +0.0081249997 0.039515998 0.045373999 -0.24138522 0.43614018 -0.86689961 0.0099169947 +-0.0016111 0.049844 0.051720001 -0.038185336 0.39012834 -0.91996837 0.010735004 +-0.01245 0.046773002 0.050903 0.0094410116 0.079695806 -0.99677449 0.047492072 +-0.013851 0.039778002 0.051036 -0.20715107 -0.25438344 -0.94465739 0.025480686 +-0.0020294001 0.044874001 0.047587 -0.14519539 0.31627905 -0.93748909 0.018671323 +-0.011653 0.046859998 0.048661001 0.0094404668 0.079705797 -0.99677372 0.047494438 +-0.0018611 0.039606001 0.047339 -0.14519632 0.31627876 -0.93748909 0.018673668 +-0.0091545004 0.039579999 0.049415 -0.18494596 0.016573779 -0.98260897 0.025298525 +0.043660998 0.094028004 0.02252 -0.92041755 -0.28093216 -0.27186149 0.0066030738 +0.034642 0.10473 0.031831 -0.69015884 -0.40015957 -0.60295367 0.0044354568 +0.028343 0.1072 0.036339 -0.52822232 -0.47807541 -0.70173007 0.0067754826 +0.036339 0.096551999 0.034843002 -0.7790432 -0.26857656 -0.56653172 0.0041775848 +0.031732999 0.099372 0.038504999 -0.65142018 -0.3041302 -0.69509459 0.0061181071 +0.036998 0.10668 0.026781 -0.8241421 -0.39830247 -0.40267223 0.0091458764 +0.032875001 0.11108 0.029589999 -0.74014568 -0.52058494 -0.42564726 0.011109083 +0.040938001 0.097131997 0.026663 -0.88692129 -0.31068045 -0.34183076 0.0038380553 +0.044153001 0.086465999 0.024241 -0.95414311 -0.11537059 -0.27622551 0.0076777046 +0.053750001 0.072221003 0.020429 -0.51798862 -0.50113165 -0.69322062 0.06503357 +0.045159999 0.076573998 0.023593999 -0.69614828 -0.084045157 -0.71296144 0.044977665 +0.038036 0.086663 0.035459001 -0.81002659 -0.061518699 -0.58315742 0.0046865996 +0.037861001 0.076624997 0.035657998 -0.77414429 0.054391067 -0.63066804 0.0025961238 +0.042215999 0.087237 0.028254 -0.89833516 -0.10155091 -0.42741233 0.0056578321 +0.042355001 0.076747 0.028580001 -0.8671717 -0.077556886 -0.49193311 0.013927608 +0.043875001 0.096228004 0.015269 -0.95895141 -0.22659501 -0.1704898 0.0043888544 +0.044374999 0.096796997 0.0086444998 -0.94115782 -0.30727333 -0.14073028 0.0059855459 +0.039545 0.1061 0.017655 -0.88866729 -0.41421592 -0.19671176 0.0021756196 +0.042312998 0.10009 0.017237 -0.92437547 -0.33019742 -0.19104898 0.0041756001 +0.045405999 0.087416999 0.015604 -0.96485788 -0.19427004 -0.17694214 0.0036036982 +0.055117998 0.072639003 0.017944001 -0.56342161 -0.42417717 -0.70896399 0.053529784 +0.048721999 0.073760003 0.017433999 -0.5870927 -0.5095039 -0.62906915 0.043783061 +0.045917001 0.086297996 0.0094210999 -0.97563714 -0.16882955 -0.14010231 0.0032495963 +0.019432999 0.1096 0.039062999 -0.25648993 -0.49896565 -0.82779592 0.0047740079 +0.01097 0.11058 0.039648 -0.14828649 -0.48850796 -0.85986692 0.004848307 +0.046657 0.057153001 0.031337 -0.35205206 -0.072589144 -0.93316144 0.031428069 +0.056079 0.066335 0.024122 -0.52020526 -0.44320381 -0.730039 0.049573082 +0.048168 0.06701 0.026298 -0.2865766 -0.49326757 -0.82131654 0.032226685 +0.056054998 0.057252999 0.024901999 -0.70871705 0.068796292 -0.7021305 0.029180726 +0.051162999 0.056662001 0.029137 -0.57538748 0.071726911 -0.81472969 0.043009475 +0.036913998 0.067032002 0.036122002 -0.7568534 0.066869825 -0.65015489 0.0050594709 +0.033 0.064719997 0.039903 -0.668118 0.14268312 -0.73024642 0.0029397116 +0.038004 0.056506999 0.033119 -0.49821573 0.01963944 -0.86683065 0.042904783 +0.030629 0.054915 0.038484 -0.526851 0.27108574 -0.80556852 0.0028973371 +0.041875001 0.066382997 0.028356999 -0.78373605 -0.10993145 -0.61128795 0.036790907 +0.041434001 0.060880002 0.029632 -0.53708231 -0.022425119 -0.84323174 0.045972284 +0.044921 0.049904 0.031243 -0.3528904 0.34862784 -0.86828965 0.037123516 +0.054634999 0.050167002 0.022043999 -0.70719039 0.31820387 -0.63136995 0.016325338 +0.048280001 0.047370002 0.025845001 -0.52652866 0.51785374 -0.67423666 0.0098816361 +0.037973002 0.048347 0.031456001 -0.36187053 0.47896025 -0.7997793 0.015337758 +0.028053001 0.047061 0.035990998 -0.57399821 0.45164311 -0.68304068 0.016997997 +0.025595 0.040346 0.034150001 -0.68097848 0.28570601 -0.67427027 0.0093491748 +0.038454998 0.043508999 0.028278001 -0.37806544 0.51105171 -0.77194083 0.022927471 +0.032031 0.043278001 0.029253 -0.53197962 0.50747716 -0.67783815 0.019725965 +0.036580998 0.040335 0.025144 -0.39942452 0.69131386 -0.60211724 0.007815362 +0.03019 0.039321002 0.026846999 -0.53198594 0.50746638 -0.67784125 0.019722477 +0.059333 0.067891002 0.017361 -0.56344873 -0.42420113 -0.70892805 0.053521059 +0.046500001 0.071451999 0.019710001 -0.51798266 -0.50117189 -0.693196 0.065022051 +0.059562001 0.057746999 0.018340001 -0.88730741 0.07214395 -0.45550054 0.044594601 +0.055636 0.049199 0.019173 -0.70719075 0.31820732 -0.6313678 0.016323969 +0.050500002 0.045063999 0.019181 -0.58164734 0.58193195 -0.56836736 0.014211843 +0.023 0.047803 0.039776001 -0.60811114 0.31426507 -0.72899812 0.0063965297 +0.022389 0.038860001 0.038795002 -0.59490627 0.43003449 -0.67908525 0.018004857 +-0.019545 0.093900003 0.052205 0.34927082 -0.48287472 -0.80302054 0.018088751 +-0.021462001 0.10618 0.042059001 0.18082748 -0.437738 -0.88073081 0.0027749946 +-0.031027 0.10395 0.041228 0.11291887 -0.47436607 -0.87305558 0.0067759664 +-0.022521 0.097723 0.045194 0.2705847 -0.41650814 -0.86793143 0.023760239 +-0.031858001 0.097025998 0.043878 0.17280744 -0.2860494 -0.94250375 0.014363435 +-0.043262001 0.10412 0.040890999 0.00073640002 -0.45958629 -0.88813281 0.0045937961 +-0.052154001 0.10404 0.040972002 -0.03633894 -0.34159034 -0.93914616 0.00271941 +-0.041875001 0.096943997 0.042424001 -0.0044234376 -0.22387859 -0.97460699 0.0070257671 +-0.051918998 0.096966997 0.043563001 -0.04023103 -0.22149211 -0.97433192 0.0042004804 +-0.021489 0.086672001 0.054767001 0.50618094 -0.16235481 -0.84700757 0.034249049 +-0.027000001 0.083086997 0.050283998 0.68004489 -0.089362115 -0.72770423 0.030443884 +-0.02107 0.077248998 0.054365002 0.53606218 0.17085041 -0.82670885 0.018211767 +-0.026010999 0.089634001 0.048981 0.68004316 -0.089353099 -0.72770685 0.030443544 +-0.031893 0.087035 0.044169001 0.2828007 -0.035433222 -0.95852399 0.027427558 +-0.025625 0.074891999 0.047102001 0.69481319 0.2732133 -0.66527379 0.01750415 +-0.031970002 0.076899998 0.042176999 0.29757643 0.22898653 -0.92682981 0.031569399 +-0.041824002 0.086953998 0.043295 0.106451 0.0045626885 -0.99430746 0.028152194 +-0.051825002 0.086843997 0.044932999 -0.038542189 0.067814387 -0.99695325 0.006826716 +-0.041917998 0.076728001 0.042564001 -0.0079677086 0.18697172 -0.98233294 0.0034264468 +-0.051849 0.076876998 0.042992 0.038096964 0.28818578 -0.95681643 0.0087176375 +-0.061338998 0.10393 0.041164 -0.029402535 -0.25662446 -0.9660638 0.036180034 +-0.072672002 0.10976 0.044294 0.084962435 -0.63697439 0.76618856 0.040219717 +-0.061783999 0.096825004 0.043327 0.16092302 -0.22269997 -0.96151358 0.0058681369 +-0.070058003 0.096202999 0.041397002 0.4036282 -0.0080862371 -0.91488731 0.038544029 +-0.080439001 0.11091 0.044342998 -0.31796536 -0.75363255 0.57527047 0.0096438928 +-0.061926998 0.086723998 0.044520002 0.17248975 0.045638345 -0.98395348 0.0073326589 +-0.070344001 0.087352 0.041908 0.42582259 0.091916025 -0.90012586 0.01168427 +-0.061409999 0.077489004 0.042178001 0.19923167 0.37411079 -0.90573061 0.0082584694 +-0.068579003 0.080144003 0.041023999 0.41895124 0.42133331 -0.80433702 0.0084476769 +-0.019045001 0.067731999 0.052388001 0.65199536 0.23585072 -0.72060847 0.018073827 +-0.017742001 0.058908999 0.050809 0.55176026 0.18901981 -0.8123005 0.01541355 +-0.023548 0.066381998 0.045226 0.69483125 0.28809536 -0.65894651 0.018995659 +-0.033989999 0.067795001 0.040929001 0.05322047 0.19476742 -0.97940451 0.032201033 +-0.02169 0.056549001 0.045164 0.69635779 0.11462907 -0.70848149 0.021707475 +-0.036111001 0.060706001 0.040406998 -0.061588861 0.12001347 -0.99085999 0.010013676 +-0.041230999 0.066950999 0.041391999 0.10316385 0.13051817 -0.98606396 0.020321144 +-0.048588 0.070955999 0.040357001 0.18421111 0.32899684 -0.92618972 0.015567646 +-0.0403 0.059464999 0.040445998 0.16084772 0.15765952 -0.97430557 0.030772509 +-0.021919999 0.044964999 0.052258 0.1091383 -0.44714984 -0.88777572 0.033779215 +-0.029186999 0.043584999 0.051088002 0.29156253 -0.59619606 -0.74802518 0.039049324 +-0.021919001 0.039825998 0.053521 -0.075991563 -0.25113225 -0.96496522 0.025803825 +-0.030331001 0.039749 0.052133001 0.30103204 -0.48792616 -0.81933367 0.032826744 +-0.021997999 0.049846999 0.046725001 0.29242998 -0.44415969 -0.84688061 0.077966951 +-0.031911001 0.046847999 0.045187 0.36673963 -0.60464549 -0.70704025 0.0094668921 +-0.035275999 0.039753001 0.047529001 0.39517462 -0.55422795 -0.73257649 0.01399758 +-0.042016 0.044822998 0.041593999 0.10855728 -0.51931661 -0.84765881 0.029278623 +-0.051940002 0.044707 0.043497998 0.082651727 -0.48762557 -0.86913168 0.025466334 +-0.041928001 0.039326999 0.043582 0.15547548 -0.66415334 -0.73125088 0.02137457 +-0.051856998 0.039252002 0.046211999 0.14657496 -0.59871596 -0.78743577 0.031854171 +-0.059452999 0.044240002 0.042862002 0.35994217 -0.78289491 -0.50746149 0.050245311 +-0.060764998 0.039087001 0.044362999 0.27816954 -0.61602265 -0.73697889 0.070127271 +-0.024273001 0.11038 0.039129 0.21418424 -0.47806239 -0.85181069 0.0033514106 +-0.032379001 0.10878 0.037951998 0.10414454 -0.5102818 -0.85367811 0.00511677 +-0.041152 0.10853 0.037969001 -0.038321562 -0.47359723 -0.87990749 0.0054788273 +-0.051697999 0.10906 0.038258001 -0.077510841 -0.42872471 -0.90010399 0.0054551302 +-0.062091 0.10877 0.038274001 -0.19225384 -0.14292401 -0.97088164 0.072213292 +-0.071654998 0.10596 0.037516002 -0.12120762 -0.54344511 0.83064801 0.11271713 +-0.074634001 0.097746 0.038346998 0.57305831 -0.0035732505 -0.81950676 0.033460639 +-0.079120003 0.10508 0.032308001 0.7911132 0.17815721 -0.58514953 0.055837024 +-0.080202997 0.096758001 0.033592001 0.70376015 -0.11795598 -0.7005769 0.0059455447 +-0.083779998 0.10568 0.025985001 0.84119576 0.021250656 -0.54031295 0.033550162 +-0.087292001 0.10314 0.020825 0.87406546 -0.16717812 -0.45613703 0.0070871506 +-0.085210003 0.097079001 0.02781 0.84734458 -0.091733746 -0.52306026 0.0057304609 +-0.088082001 0.096455999 0.022985 0.90336299 -0.11470471 -0.41325316 0.0070019392 +-0.075159997 0.086039998 0.038816001 0.56156623 0.1044868 -0.82080805 0.0091117797 +-0.064576998 0.073454998 0.038970001 0.26235282 0.46205065 -0.8471601 0.0077266204 +-0.072278999 0.076416001 0.036412999 0.53422672 0.41966563 -0.73381364 0.0055706957 +-0.076375 0.072563 0.028729999 0.67109746 0.6318242 -0.38784823 0.0044638016 +-0.080031 0.087076001 0.034290001 0.73211843 0.15737711 -0.6627481 0.010024117 +-0.078919001 0.079370998 0.032476999 0.69276303 0.45652202 -0.55827153 0.0087372195 +-0.084834002 0.086686 0.026974 0.83963245 0.18885627 -0.50926477 0.0076140678 +-0.087890998 0.089233004 0.022611 0.8909108 0.13929515 -0.43229026 0.012848299 +-0.081047997 0.077169001 0.025829 0.78641486 0.46373811 -0.40804234 0.0062469114 +-0.086392999 0.10784 0.018634999 0.82173651 0.22532307 -0.52342957 0.049227774 +-0.087672003 0.10492 0.017263999 0.86681664 0.0054754531 -0.498597 0.033453744 +-0.089332998 0.098483004 0.01761 0.90338933 -0.11469468 -0.41319811 0.0069744457 +-0.086374998 0.083067 0.018607 0.89179856 0.414648 -0.18100342 0.0088002766 +-0.089179002 0.089185998 0.018947 0.97110236 0.13235515 -0.19860105 0.021748984 +-0.082878999 0.076109 0.017794 0.84889227 0.48539269 -0.20922697 0.0035174063 +-0.082500003 0.074674003 0.0071175001 0.82750958 0.52928513 -0.18731017 0.0049080662 +-0.026436999 0.064140998 0.039321002 0.43814281 0.22409004 -0.87052548 0.040726949 +-0.030035 0.066129997 0.038942002 0.042627886 0.20684171 -0.97744536 0.036731411 +-0.026131 0.056531001 0.038881999 0.46868646 -0.18745917 -0.86324513 0.057940684 +-0.031663999 0.056657001 0.037742 -0.011200796 -0.10971908 -0.99389958 0.049078658 +-0.045715999 0.064540997 0.039166 0.27066356 0.20376664 -0.94086152 0.014609119 +-0.051959001 0.066868998 0.036733001 0.45068917 0.51399606 -0.72985429 0.025667811 +-0.042557001 0.055544998 0.039026 0.1486076 -0.041601405 -0.98802084 0.041527726 +-0.049405999 0.056892 0.034343999 0.6727168 0.11576855 -0.73078698 0.042428575 +-0.055500001 0.062391002 0.029498 0.59951371 0.53357315 -0.59655923 0.043033648 +-0.053750001 0.058573999 0.026312999 0.83461094 0.44335803 -0.32689163 0.075001374 +-0.034060001 0.050136998 0.038577002 0.062156912 -0.34150508 -0.9378224 0.049006518 +-0.041740999 0.049589999 0.03929 0.18698144 -0.34636065 -0.9192782 0.045123689 +-0.050974999 0.049435001 0.036965001 0.24271692 -0.67756188 -0.69426107 0.051133763 +-0.052999999 0.051065002 0.029208999 0.3495166 -0.61714137 -0.70496428 0.042339273 +-0.054145001 0.054568 0.012257 0.73811674 -0.5837099 0.33832872 0.10117272 +-0.055847999 0.054170001 0.0083272001 0.55382234 -0.62194115 0.55359733 0.074788764 +-0.054843999 0.049295001 0.011462 0.71343958 -0.5019455 0.48893222 0.076894961 +-0.056150001 0.050618999 0.0092928996 0.58453882 -0.60774499 0.53755039 0.087734967 +-0.061450999 0.068256997 0.035376001 -0.29371658 -0.70654899 0.64383167 0.0069489204 +-0.069724999 0.069958001 0.032788001 0.51250762 0.6654157 -0.54273182 0.0082743121 +-0.062822998 0.063322 0.026885999 -0.36954576 -0.80908924 0.45695794 0.0015373469 +-0.071037002 0.066786997 0.025227999 -0.53203923 -0.76704764 0.35856968 0.0046437387 +-0.060857002 0.060568001 0.022643 -0.36452094 -0.86008775 0.3568942 0.0045510111 +-0.067000002 0.061558001 0.020109 -0.45134526 -0.75177139 0.48075694 0.0056222733 +-0.078199998 0.071278997 0.021032 0.72853959 0.59948909 -0.33142552 0.0062416438 +-0.062116001 0.045145001 0.037802 0.53365648 -0.68499517 -0.49597609 0.040642425 +-0.065472998 0.039512999 0.037964001 0.53365141 -0.68499339 -0.49598423 0.04063797 +-0.067249998 0.037420001 0.033413 0.58283192 -0.78510177 -0.20957632 0.040994368 +-0.072701998 0.065008 0.018701 -0.52006978 -0.77749521 0.35359377 0.0054407413 +-0.061450001 0.059165001 0.018731 -0.34448564 -0.88180333 0.32210648 0.0041377745 +-0.067500003 0.061478999 0.019221 -0.49395499 -0.764835 0.41356477 0.0061281542 +-0.057411 0.054113999 0.0038256999 0.58451724 -0.60774255 0.53757656 0.087737001 +-0.079222001 0.070653997 0.017735001 0.73846012 0.65186685 -0.17247072 0.0068075666 +-0.062472999 0.044679999 0.01111 0.46874011 -0.60717469 -0.64157742 0.005866745 +-0.067249998 0.042257998 0.010414 0.46878213 -0.60720211 -0.64152074 0.0058589075 +-0.066389002 0.040514998 0.01316 0.46878806 -0.60721052 -0.64150858 0.0058575436 +-0.068359002 0.038502 0.011958 0.46882352 -0.60723507 -0.64145941 0.0058502122 +-0.061381001 0.047479998 0.0076069999 0.5694347 -0.66778845 -0.47937754 0.016703486 +-0.068558998 0.043549001 0.0081575997 0.47598499 -0.67020458 -0.56944197 0.0028901543 +-0.070928998 0.039829999 0.0085888002 0.47598499 -0.67020458 -0.56944197 0.0028901543 +-0.016625 0.18375 -0.019734999 -0.69538468 -0.23898028 -0.67773783 0.0056980988 +-0.015198 0.17471001 -0.018867999 -0.5923745 -0.24637972 -0.76706547 0.01349183 +-0.015944 0.16264001 -0.0091036996 -0.43222958 -0.51712793 -0.73875326 0.00369721 +-0.015977001 0.16069999 -0.0088072 -0.39357325 -0.48935169 -0.77822554 0.0063953851 +-0.013251 0.16708 -0.015264 -0.52248943 -0.46689072 -0.71345484 0.006487147 +-0.014292 0.16098 -0.011252 -0.45074272 -0.5343765 -0.71503335 0.0045233937 +-0.013986 0.184 -0.023739001 -0.69996727 -0.29157621 -0.65194255 0.0068006245 +-0.011633 0.17699 -0.023349 -0.64671504 -0.3023999 -0.70022422 0.011895906 +-0.0091028996 0.16988 -0.021457 -0.59230733 -0.37863559 -0.71120119 0.0092242574 +-0.025562 0.18273 -0.0096247001 0.67986989 0.016672963 0.73314315 0.0095390202 +-0.027249999 0.18254 -0.0094384002 0.67986888 0.016679294 0.73314404 0.0095392438 +-0.025736 0.17948 -0.0089653004 0.67986959 0.016676841 0.73314333 0.0095393462 +-0.031215999 0.17589 -0.0051154001 0.63255119 -0.029571004 0.7739538 0.017594624 +-0.020399 0.18449999 -0.014943 0.70246118 0.038495716 0.71068025 0.0076522529 +-0.021338999 0.17645 -0.014566 0.66117793 0.027738377 0.7497161 0.0099074291 +-0.027124999 0.17234001 -0.010156 0.60842365 0.096019991 0.78778219 0.021824442 +-0.039390001 0.1733 -0.0023574999 -0.29537752 -0.14131819 -0.94487101 0.06837032 +-0.022876 0.16406 -0.0078103002 -0.38129494 -0.48171195 -0.78902966 0.006038486 +-0.031597 0.16651 -0.0049291998 -0.62478781 -0.1951244 -0.75602031 0.019099949 +-0.022600001 0.15911999 -0.0037990001 -0.37730029 -0.50651139 -0.77530044 0.0046436773 +-0.030371999 0.15767001 -0.0012672 -0.46141326 -0.12661076 -0.87810451 0.027269369 +-0.021158 0.16848999 -0.012383 -0.43273494 -0.47357085 -0.76711875 0.0067598876 +-0.027000001 0.17120001 -0.01022 0.60842371 0.096020497 0.78778213 0.021823406 +-0.041719001 0.16813 -0.00074957998 -0.30108872 -0.091772109 -0.94916987 0.064206578 +-0.048250001 0.16748001 -0.00015191 -0.16683987 -0.35772914 -0.91880053 0.042867519 +-0.037250001 0.16147 -7.2627998e-05 -0.50185984 -0.16620237 -0.84883064 0.034010869 +-0.066428997 0.15783 -0.0085672997 0.2064072 -0.93305117 -0.29463804 0.015543612 +-0.071284004 0.15839 -0.005998 0.2064072 -0.93305117 -0.29463804 0.015543612 +-0.065978996 0.16288 -0.017792 0.19103831 -0.86545706 -0.46312907 0.031841654 +-0.071622998 0.16384 -0.015760001 0.19091524 -0.86551589 -0.46306965 0.031785745 +-0.066068001 0.16051 -0.013567 0.18612653 -0.91787857 -0.350508 0.025151037 +-0.073307 0.16049001 -0.011832 0.23880717 -0.85915995 -0.45256522 0.034051653 +-0.077 0.16204 -0.019241 0.22228232 -0.8792305 -0.42136011 0.03356503 +-0.077179 0.15851 -0.01495 0.18266542 -0.91310471 -0.36451766 0.025214965 +-0.073691003 0.17286 -0.037944 -0.025542269 -0.91648245 -0.39925876 0.019174108 +-0.077550001 0.17220999 -0.039175 -0.025476603 -0.91650516 -0.39921072 0.019112799 +-0.065921001 0.16586 -0.025022 0.15715888 -0.88196146 -0.44434786 0.02887252 +-0.072094999 0.16784 -0.024724999 0.1767879 -0.88837665 -0.42371312 0.027746443 +-0.066 0.16808 -0.030916 0.054610178 -0.91556865 -0.39843675 0.014395946 +-0.073448002 0.17050999 -0.032044999 0.098481193 -0.88386661 -0.45725399 0.010965914 +-0.077770002 0.16434 -0.025938001 0.21544355 -0.87500358 -0.43353528 0.026826311 +-0.077892996 0.16039 -0.021299001 0.2222922 -0.87915635 -0.42150956 0.033694346 +-0.078211002 0.169 -0.034566 0.18613139 -0.85368556 -0.4863908 0.012735247 +-0.034667 0.15131 -0.00071028998 -0.39771387 -0.18987525 -0.89764744 0.019185906 +-0.066117004 0.17353 -0.047453001 -0.15292998 -0.88486844 -0.44002312 0.012427152 +-0.071985997 0.17612 -0.045384001 -0.1860113 -0.85304689 -0.48755604 0.015654244 +-0.069250003 0.182 -0.055025999 -0.18646584 -0.85284752 -0.48773098 0.015722066 +-0.064992003 0.17802 -0.054644998 -0.18632874 -0.85291398 -0.48766747 0.015678046 +-0.069935001 0.17983 -0.051987998 -0.18646584 -0.85284752 -0.48773098 0.015722066 +-0.077930003 0.17516001 -0.044399999 -0.18629308 -0.85293317 -0.48764735 0.01566753 diff --git a/test/colored_cloud.pcd b/test/colored_cloud.pcd index 6358c1c84c5e599625f032660f3fad41411b89ec..055f90b5678f9ba99908587ff23b3c39dd0e9cab 100644 GIT binary patch literal 32241 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 Z{Ni*x$>DeDPfj7=q-%sM^Ykd${{ih600RI3 literal 14162 zcmeHsiCYxMyKYZ6%)o$*iV8|hH;X}oii(O6rlx^W#C_k;Ff4-X;0hwL3&^ITf+G74 zA|M!4plaArR8&m(C7OVUqA_ZsG5(B6>Q?Ky=iKK$=RWuR0XolgPgi~QRn_}_-}_dJ zDK~SqgOb}fYo;|fgIgWw9k7RU@C)4Q!6{w$xN@G}emgkNz+L{Xd!!o|tzGA|Y9&WZ zrHh>wEmyANLby=w0JqD_%~iV2Y3m|x?#GXG{g%~>xWykn8>9}aHmqFF&Hnh2ZgNtt zU&76nE?MN{xP&y%nl($h(P`19)vKI{)vOPn*&pxH4{hr{+9>VT+i`B*0j|43UEBlx zgLduMy?cj;bg4N-MFNUq%Z9h2_!(P~*S;-ie!&*B@#oFRo!N#QmN=s+DO(X$md9!u zZrzIhtl-SGJGUb*>2|bpnCX|r{9=-FEy zUAN`YxhMrn?gYqosvKc|TcobDK~e0xg(xQCE2Po609oIC3NmK?3>@{If$MvI0eJm$ zV9CyV0o=yF1Sj6S1YO_$3Tk6tfx;cH!O6wHgD=1zz(C5q1&8kb8(22H19q~{-;Qxc z>bYN|pMtib+;5!Gy=U9eXO`R1wq4th-*;PGWn%xYw;+#*Ee~{M^h^0V(oom628D^& zKG2d$W45kDu{kSH+|Y7YnUr3*9NC30LviY*4=5SE^vDud8GU^5VpkcPep87~ja8y) z0rn{Kj6FK@lO4)3u|qkYJj$(5AT~$`pw+A8Xx2$vH0B2zWT#k&L~&oCmTL=8^ymeM zwP`m%yQdkT^3(}P8ZsWCAIG7+4&%_5<@#t)Pam-vd&Z)rKa4?@U8B*^jM2#8gbXeJ zQx8R}^iZ;`9-5>q7=?NdjzYV3k3tWYjY4m$b&=jUT{I;`$GDf(LDWqtii1*AcT9@z zMM%-H2inN^f;L)`t&O5avr_bZpf=(hwa|o82^vh1pasvx$frS!>?ep33}?ki+eM5f zD8wirOA~dz)j;fe4J4hdfjW{IbjOQj(6d$=8J?w)cn^&p+z_EF<|1?=ibAoQ8^D|S z>?LsT^<}VOcO!6vO<;UZGsycHg3#>lOk7pS6JF?^=NGj#lv3 zwszont^*wZi|qv5pWT34+zs~A-QdfCZt(lxcfswQ-vY&{Z^5h(_B&v_wI9f{z6Fck zd^-o!_C9#K@+a`rasb>LJqT|6HVDw22cWiY2o#-Q zAA;k1he7PdN5JXIW6+p20;Y#Q0V{rZ0u+r;!JQJxGvHPKGdO)Bx{m#WeF2_OFToEF zUVH$R=nd3a?v%;2+C}s5kWrr8K)zPc}G91!q5DmN@8r2}$>pC>Kf!6w$?hw}?-lDIK z2LIL?8rLB9f2WBy-P3ey(0p&GiGJT3-9YcJVcZ(%@F%oegGl4^A_~Ykc>7fvcr@uK=zo6{ zT)Te^l=8>H4_rFX6`cU=an(r>Yj+ClGsyrunM~k3oC%ydP6L~gGhk+X7GQgavH)&9 z3+5DN1I=kU;G$M8*!d(E{ArpGW@r=uHX^Ro7iIDBrXB(9+iM|9i>23 zTn5L1M@+;QhAefc^a!z;-750vddtgNGAdg3~i!f~3*E0t=sC zf&bm#fZmK(K)g2dHBcV-ojB9`r@{rf{po_nnW)gl9WKLEpZL{y7j$yd*B*UZJN|M; z-M|@nn8&jzZClWD%`IrRr$BnECn`{#GeG(| zaTeMgt^=w+;xG$J03{~;nC$d6$U(Xd7@of!HL>D+nyA2`hP(%;>`Ng&d^ z-F_Wf{Swegi)ru7}FTI_Pu!!mw^X zovd9+ZlcliWBXalfI@f^6vHK%MevnpAq;FTfB{zu;P-L)P`M@#($$~k4r{z;6Sr2w zHRmhgXubmW-Y$bF?xk?`n__tFheD_mPyktKa~@Rxk^>{6vf;kqEa>lX8czEw13pPV z1&b|CLe{k{9Zq&W4u5=g6rMhk1~;0d!m(G9;hi-}F!gC7WS2%Iz=es2VTef_tZR&c zzpamkmM1AOs}hn3E9$Yw8uE3B=d zB5W>X7ogcNz+ooLz40YHW-t}Tx=w-CE~e1!4GT9YCPUUSaS~kEX8^yLJRTZ)jfFb3 zGB_ht7iM>8!%<@;kPZJ@13s@1!NwPVH~+o+ZFB$FSIrk{e`y{GecaqU`9U+A+j6gY z?~d=9H*4Q+o?X?`?7X|9*?O|Nc}&-pX7jH4W_{7V>gKU=<;_nQ6gA@oIn8%pWj0TF z$TpuJNNG0Jw`_ho*}S=GD%+gdGNIY|j$X6TLveHS$lq*}gZ7K2wl9Bba$0_~>6$-m zGS#ncs+gMFq+5~JbWlHXa#NegwW-<9t|>Ibr0MV-x@mn3=om_YPa{0QRjdJ-hH@VKHHh+1` z_z?M6+bB8b#mYVUlH`|)kIDyLpOoKtnk9eepD%BXDV66|R>{jY)Uk5E*30s$E+~I_ zy-hyrPPcqd{|)({(mQfnoqqXk%X_SRqQ#(m?H7;aFXlg$KltpWe5&E^@<9D}@^Za@ zyY5P@OAH)1W&A0Y+jeRP5GE*ap4MuWVY`arp80(jDB$bzIfM!>RsGWfie z1N%>zfR*uP;P>}mfHOhU0RPB>1rI7TfILwQ(!I36%SBSK%uE+NRE`3ToD58M8_RZ3vSZ0!1RC325^!Ucp5zq>~Eb9Hl11kX89}xR{b_C7*!z$FAgfe%~f_F z(ZC)&8gT&Jl*OPp!4c3~oLJy8Z5e2Mw;Zt7R)VlItASzj8t_NbI&gRE2Jq8`jVx%6 z+zhs#-U8qbXW-bd4TSvjHPBe80y`$Ufv#cq$zW>D4iFRO1s*N-28+k~f?o{$K<@*8 zP+1uWQlIDzNF+sJVAwAlEEy07SVw~GMo~{R2IvHv*eBG0$mr1%(SS~MY9hG!_y|a@ zFh**IzO~Lo#n5YKPZ^{b+W$4DVnn}XapsyT6V$u#L!;F`vq24JkTDvR=u46Vx4`hZkqX|L0A@rcesrx9j&kmV$~plXei_-DglvQi45Ux=wGSFHw4Z~} zMRwrMD}-$iGQ`4~&)^xpkY_}*r<+udI%SM9PJMyyw3?#JeN)KF>ZS`Y@&&31bV=+Oah zH3xMWwFQasGM=F;ZV3pBzIV2a;22Hq5iR7p@kkGKY|bY7*=nhZ(Vlc)ib7xMpru}s zB=%&)9anAIUDY_6IF=q-JO;%*AA{;c#uEG= zG5YA|3Vk$b{y1Xd)H05k(Btn>!p(oFNTovn5fYN_x-24#C9oRsjvv#86)>iod<9!x z#uFHLE1H`|lSg}BHABrd3F8^Y&%X-_Njv%>R^Zwcv82`+Vb2^mhN zVl>y@Zij!m4vH8$|5cj+Qr5CIGGb6dN`#bx1?_OZl{}OD)E5SjwFyw04;nGN4gIs9 z&_@qla6&C zceaW=c#*F=DLK0|-jOU!9mek_lkQjRsI5pG4Tx7$VqwiQ+Wkrs)F(DYld2*adi(e( z!m7synh8rFWkVLNL(F7kkOdha;AAw&GQ6dl;jM@_Az5S$-$Os{5EFpb+lYlNgkRSb z04mt7Ddd+Wui%%_Xs$$Xy%I@?-GGx&W0x*z1!k?v-9$WV*4j#uT~{PYhc!B+t3pk# zQk!MEsOMW<Kxbtai1ev_n+ z;wFp}rZjgHVl2LACPMT#!cP(lHOT~m%OGRGuTskfB@!VUnA{o+$qseM<}CPibbY$$ z1EGmkZy5BIhXz^K*gg%{K)c;)hCX?p6P)Yb{9M>-yZPs&H#}-Gaj*H+z0c6Axr5Uf zx+>LZ@H0l!^T{N{+7D_n^!Hg41@jYc3&4qXn7ZPjh1B-7j49D3D+snjz515qo5nsMeO zN+J(}G#CkdNFu^K-q{OD1KqjqCc!46>V|+o3AiDE?UH$YjuF)(fT4)#5rBMu|C)+0 zJlQ@Qa7{3ww_GJOcCRZ^Lc+>O=*A1(g87g`H}RI0-9hwxN(vVBlPMk>yVDeG)hkSUZ&O~i@K03uc-K;ojHX2scd^@k^68y|&` zrSH9i{}Ulp%!4XMbK}Du3VT(gKp;d1e=YSCmN4$T7n$k8uAM5zPG)D$2dZ6Y>%TgnDBX};iTZ^riV2ll#Fr>&i&Dk+H-*tPyUK_~{Dg9H z{rXBd7)w#m47>8@f)$$ij0 zQQMnnSu0C6`Cp1>?={418kFhK8Wvu}o7$A54vEPB7QEI=go~{y!DY-q(INNyFA8k~~8?b%b(F1^VI{%4Ude9MW$X zVj72J4fOhl#cqxE4fcp9k>i;mNh39Hh;E>D=}I#%jY>s?&pCqLM!hPB)~AIX`KlTFFrJ~~ z)II{l!Ezs!fVsEOS4oOzi0L6D#tJFPIG8?Un1=$LwAm0$WrO+z1~uc*s3ti~RERfF ze=G)4eu4}QGgiuU0@X+TX%xWX86`PUkX-ps{ZTgXg8H6V;-)}=Mb6(V? z!KnD)HUrh<$)4o1AOwyEJY&C#d|h~k-j)_ESm@1&7No#Juj038#lP3Z zD5U){ip^ienhog2DSk9NtdL9N6-j>aCi+yumtIsNq4tsYiHao;jwl`$Cn^4zn#_e! zDKl6qg-fSWO~bU(6#HSCBGK-ssTXyOEcLJQW5z~W#}!d0(-kFICrC?A{z=Y-I%S+i zWpK_^rke}B+KUl=6V*bD^zhYDD13lt2(#|03gPsxas zd{lvfRsI4lH?R8%zO;lZ8|V9hqzMx|k@S(1PUnbP@mbbqj_VM?} zJ2(m9=>Q+RBhZ`E(e=fi{sGt{z#n@B1aSI{KlTjb7*U`fr$hOv3^d#W0q2jK=Kr4a6S4LDCQ{{je7&p}aYU_Vw}QjJQIM(RTOs#jZR3u;)(Bg1Ac`<>iY#ynL{$w>Ng# zNrYppYKJ#gd3a;j9X?p)>4U?gqsYDwL`M^!k@!$_1c5{v8hHqZL>$6lhr(5i;lA)g z_&|6B4m}u-_l6zBdqNN3oq++^mwQR?2nr;adIm>IC=X7b_6i6h<68`PbIRRV5ObFH z_6y`*8(VXhys4eJojU>1<#SK}AiUEr82k9{!k#|C*xe@xd-w!kS6_eZ=I5^>B}FzS zqr8ocC{JU-wS;mtwz9L)bln+*UA>I!G`#$=`%Yt1I~gJ2oyG>n653^ludz-Z!?^q4 zuQ_J2pR+qYurC59pFWH8i?VT6ZZ6KsE5teZWw@XulA*H;3RMPdPHq9t&MUzM#U(hq zs1#?M&c@lNPvESq4178(3yaTYoyFN_b4alB&gSC$Y?5Kr8IF{{I436$XXg}f>!^GJ zVQyZYN?$W0HxHl6Cdwzy$jZkjvWRwSW@hE$j5ETxqa?26oOdRNW9b}xjN7e|dNvQA zPQlfJ(7$gk3{NA6O)o~d_p3QNjMTI(Tq>PvGIpFMjDGF;^J_0++iHX zF`AL_@i^jeBtsqKOzA_16IC+aNE{v$hr?oH$y`w}u_ozMj8HbAQPCvC;ZZ}PYARf1 z%E?4g5uAZm1P+Uc!21s!QZe>*^#1UJgfI3TJcvWX!tmY$2k`EY18&tK?_iU4R8Zsx zITY;-O**+H6?C6eRoGnOxhgY zQ{E=kgwBY;figtLLR-5*Uyu8g^IPxbKd&H2$cQd^wa`iE7u&-mheXxsZALU|F zO}UsNN=2&Bl_NSwZ+G`JX<*!N=$_~X-MqqVd^Rr^=j0XPVv~k3C52@;zqkzN<`>~J zIr%t0*S?o3F&U7Q;JngOoPWk7P&4y1&dxeRR-2b)(pAvVS|}pXTxC*9=jM|5^DM7V zWt-%Za%Ko+ktEB`F=?Q3ROVEsiEtXJ<0aHFPUebWSGc&Yr|avyb7lj8q&Fs=~WMwqmbs0J}NO!`tW2 zz*}aq=6IEb30`Ex;;(c+!!z|K;hB>Ru%(G1o^N4@h^Bj-fnd(*k7P8u zJGyZfXT)~4b>r5~Zrs+@jXOKLa9d|5?(FEm9qnznwY?S7>W&u9!4Tp0c8J^Bns9sD z72MWx39Ho?aT}^9e|Nlqo11EJslEzduBpKF=gV}{h)OV%fXL5q$}JmP`q!|_Rkq!V#$LV`yRZ=k$KdZH5INomL7n8Szh zp}2T_kSrtIY=mT4nArd+B1WY{$Hrmet&e!_A2#aqxF?E=G8>UZ;sa5K@WDv)B#lFn zgz1a=sc`e%+Tme%_rWk6av;nrLK+epiuZ(sV85VX!U;QqgUx!VUF6r!pj|3`mkxtDzOTbAxORnORXLE2$R-VT*-J_?o@X5>*_~_A8 zoMaNlCdEbL!_nc`%VQ<>+-8qAlD`Ez&f&1#XHsk)SOd?Oh_UrpZT!__eQaei z8P6~`#+K8k;5l=qVRpXN47_meEIik0F1EC?#&hP{;Q8}xu+3asY-hz|vD|7A{>sXR zvlurUTgD?+2@FNW<(?Ufvq%MG6X0?<;Vp)d78aG`yrK%6TXaq(5f>Jg~> zhNxFqSc(e@N^x#M2|im;ES$^o3QJT*RME6FH4|y2QGs&`&yi%#E+j9Ryfu|;u1}xM zFXXDJ0`Fc&mP~W%i)Z9}k2?K;ez`ctDbVgAdnz**?^;jmB9yykQ6OU%#l%uCeS|z5P#3^YfaQx8|_%JD?#3|0wgp_m~pPY^lC#A>s zOxvw&q!W&$c$8|yBqZZQW*M4MaYyjMm}yp`m;@ru@i;PO+9r*tI2;AVO&^n7PoiTtZm7S6>+3m|6JAA_iB)wMam|H# zTy>!iSJu?w^VJt{WpypCI?qYQRMq2>$~s(8c>$N5tHs6VYD^nP*W&Vu3%IbN1{ao} z$ED>}oWyuwe0e3#Evv-YWo0QV?L}Nt^9`=3xrj?i8dUO(M)CQ2 zT+W#ju`J;%L>1LsH9_lqbqzj${yeU!s>LPeI2(?k%PYSjsFhW460VJ^;1FF^Uajh* zO1XZ1gl9zO&l&4*@2M&-kWjn@Rbgzyo^NQx7cN13v9SZ!!%lob-Gv)kyK!UNReZVQ z8fGtbT*plvH}IA2oA}E0Ufk4k3%6anh0(R!xV`5l)^5Lc9d}*r!JXaY$i~a4tHyoe z&h8tyqq|onq1!uqraDsBA{lOzMtesOZYBgRq0}5vf+b;UVo%bXRWqWNPENsDEGebB zr)H4kY~dtJGI$By(a|+Eg6f(YZ(=hQQJqt}gg3<2u509&*w%dwcVA6-x#XTkM@u(u zntG48wP71uyD)6!+NxDhsE*mMP7YQBUU(Kq-~V?EYsxKx8Ld{c#ME|y{H)4xv$ nd_v$80-q50guo{RJ|XZ4flmm0Lf{hupAh(jz$XO$zX<# #include #include +#include #include #include @@ -51,6 +52,8 @@ using namespace pcl; using pcl::test::EXPECT_EQ_VECTORS; +pcl::PCLPointCloud2 cloud_blob; + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TEST (PCL, compute3DCentroidFloat) { @@ -1009,9 +1012,80 @@ TEST (PCL, computeCentroid) EXPECT_FLOAT_EQ (-500, centroid.curvature); } +TEST (PCL, demeanPointCloud) +{ + PointCloud cloud, cloud_demean; + fromPCLPointCloud2 (cloud_blob, cloud); + + Eigen::Vector4f centroid; + compute3DCentroid (cloud, centroid); + EXPECT_NEAR (centroid[0], -0.0290809, 1e-4); + EXPECT_NEAR (centroid[1], 0.102653, 1e-4); + EXPECT_NEAR (centroid[2], 0.027302, 1e-4); + EXPECT_NEAR (centroid[3], 1, 1e-4); + + // Check standard demean + demeanPointCloud (cloud, centroid, cloud_demean); + EXPECT_METADATA_EQ (cloud_demean, cloud); + + EXPECT_XYZ_NEAR (cloud_demean[0], PointXYZ (0.034503, 0.010837, 0.013447), 1e-4); + EXPECT_XYZ_NEAR (cloud_demean[cloud_demean.size () - 1], PointXYZ (-0.048849, 0.072507, -0.071702), 1e-4); + + std::vector indices (cloud.size ()); + for (int i = 0; i < static_cast (indices.size ()); ++i) { indices[i] = i; } + + // Check standard demean w/ indices + demeanPointCloud (cloud, indices, centroid, cloud_demean); + EXPECT_EQ (cloud_demean.is_dense, cloud.is_dense); + EXPECT_EQ (cloud_demean.width, indices.size ()); + EXPECT_EQ (cloud_demean.height, 1); + EXPECT_EQ (cloud_demean.size (), cloud.size ()); + + EXPECT_XYZ_NEAR (cloud_demean[0], PointXYZ (0.034503, 0.010837, 0.013447), 1e-4); + EXPECT_XYZ_NEAR (cloud_demean[cloud_demean.size () - 1], PointXYZ (-0.048849, 0.072507, -0.071702), 1e-4); + + // Check eigen demean + Eigen::MatrixXf mat_demean; + demeanPointCloud (cloud, centroid, mat_demean); + EXPECT_EQ (mat_demean.cols (), int (cloud.size ())); + EXPECT_EQ (mat_demean.rows (), 4); + + EXPECT_NEAR (mat_demean (0, 0), 0.034503, 1e-4); + EXPECT_NEAR (mat_demean (1, 0), 0.010837, 1e-4); + EXPECT_NEAR (mat_demean (2, 0), 0.013447, 1e-4); + + EXPECT_NEAR (mat_demean (0, cloud_demean.size () - 1), -0.048849, 1e-4); + EXPECT_NEAR (mat_demean (1, cloud_demean.size () - 1), 0.072507, 1e-4); + EXPECT_NEAR (mat_demean (2, cloud_demean.size () - 1), -0.071702, 1e-4); + + // Check eigen demean + indices + demeanPointCloud (cloud, indices, centroid, mat_demean); + EXPECT_EQ (mat_demean.cols (), int (cloud.size ())); + EXPECT_EQ (mat_demean.rows (), 4); + + EXPECT_NEAR (mat_demean (0, 0), 0.034503, 1e-4); + EXPECT_NEAR (mat_demean (1, 0), 0.010837, 1e-4); + EXPECT_NEAR (mat_demean (2, 0), 0.013447, 1e-4); + + EXPECT_NEAR (mat_demean (0, cloud_demean.size () - 1), -0.048849, 1e-4); + EXPECT_NEAR (mat_demean (1, cloud_demean.size () - 1), 0.072507, 1e-4); + EXPECT_NEAR (mat_demean (2, cloud_demean.size () - 1), -0.071702, 1e-4); +} + int main (int argc, char** argv) { + if (argc < 2) + { + std::cerr << "No test file given. Please download `bun0.pcd` and pass its path to the test." << std::endl; + return (-1); + } + if (io::loadPCDFile (argv[1], cloud_blob) < 0) + { + std::cerr << "Failed to read test file. Please download `bun0.pcd` and pass its path to the test." << std::endl; + return (-1); + } + testing::InitGoogleTest (&argc, argv); return (RUN_ALL_TESTS ()); } diff --git a/registration/include/pcl/registration/impl/correspondence_rejection.hpp b/test/common/test_colors.cpp similarity index 63% rename from registration/include/pcl/registration/impl/correspondence_rejection.hpp rename to test/common/test_colors.cpp index 7c89862b..e21d89d2 100644 --- a/registration/include/pcl/registration/impl/correspondence_rejection.hpp +++ b/test/common/test_colors.cpp @@ -2,9 +2,7 @@ * Software License Agreement (BSD License) * * Point Cloud Library (PCL) - www.pointclouds.org - * Copyright (c) 2012-, Open Perception, Inc. - * - * All rights reserved. + * Copyright (c) 2018-, Open Perception, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -35,24 +33,29 @@ * */ -#ifndef PCL_REGISTRATION_CORRESPONDENCE_REJECTION_IMPL_HPP_ -#define PCL_REGISTRATION_CORRESPONDENCE_REJECTION_IMPL_HPP_ +#include + +#include +#include -/////////////////////////////////////////////////////////////////////////////////////////// -template void -pcl::registration::DataContainer::setInputCloud (const typename pcl::registration::DataContainer::PointCloudConstPtr &cloud) +TEST (ColorLUT, Glasbey) { - //input_ = cloud; - setInputSource (cloud); + ASSERT_EQ (pcl::GlasbeyLUT::size (), 256); + ASSERT_RGB_EQ (pcl::GlasbeyLUT::at (0), pcl::RGB (77, 175, 74)); + ASSERT_RGB_EQ (pcl::GlasbeyLUT::at (255), pcl::RGB (117, 143, 207)); } -/////////////////////////////////////////////////////////////////////////////////////////// -template typename pcl::registration::DataContainer::PointCloudConstPtr const -pcl::registration::DataContainer::getInputCloud () +TEST (ColorLUT, Viridis) { - return (getInputSource ()); - //return (input_); + ASSERT_EQ (pcl::ViridisLUT::size (), 256); + ASSERT_RGB_EQ (pcl::ViridisLUT::at (0), pcl::RGB (68, 1, 84)); + ASSERT_RGB_EQ (pcl::ViridisLUT::at (255), pcl::RGB (254, 231, 36)); } -#endif // PCL_REGISTRATION_CORRESPONDENCE_REJECTION_IMPL_HPP_ +int +main (int argc, char** argv) +{ + testing::InitGoogleTest (&argc, argv); + return (RUN_ALL_TESTS ()); +} diff --git a/test/common/test_common.cpp b/test/common/test_common.cpp index 5976ac80..179b7b66 100644 --- a/test/common/test_common.cpp +++ b/test/common/test_common.cpp @@ -57,15 +57,13 @@ TEST (PCL, PointXYZRGB) PointXYZRGB p; uint8_t r = 127, g = 64, b = 254; - uint32_t rgb = (static_cast (r) << 16 | - static_cast (g) << 8 | - static_cast (b)); - p.rgb = *reinterpret_cast(&rgb); + p.r = r; + p.g = g; + p.b = b; - rgb = *reinterpret_cast(&p.rgb); - uint8_t rr = (rgb >> 16) & 0x0000ff; - uint8_t gg = (rgb >> 8) & 0x0000ff; - uint8_t bb = (rgb) & 0x0000ff; + uint8_t rr = (p.rgba >> 16) & 0x0000ff; + uint8_t gg = (p.rgba >> 8) & 0x0000ff; + uint8_t bb = (p.rgba) & 0x0000ff; EXPECT_EQ (r, rr); EXPECT_EQ (g, gg); @@ -75,7 +73,7 @@ TEST (PCL, PointXYZRGB) EXPECT_EQ (bb, 254); p.r = 0; p.g = 127; p.b = 0; - rgb = *reinterpret_cast(&p.rgb); + uint32_t rgb = p.rgba; rr = (rgb >> 16) & 0x0000ff; gg = (rgb >> 8) & 0x0000ff; bb = (rgb) & 0x0000ff; @@ -94,12 +92,11 @@ TEST (PCL, PointXYZRGBNormal) uint32_t rgb = (static_cast (r) << 16 | static_cast (g) << 8 | static_cast (b)); - p.rgb = *reinterpret_cast(&rgb); + p.rgba = rgb; - rgb = *reinterpret_cast(&p.rgb); - uint8_t rr = (rgb >> 16) & 0x0000ff; - uint8_t gg = (rgb >> 8) & 0x0000ff; - uint8_t bb = (rgb) & 0x0000ff; + uint8_t rr = (p.rgba >> 16) & 0x0000ff; + uint8_t gg = (p.rgba >> 8) & 0x0000ff; + uint8_t bb = (p.rgba) & 0x0000ff; EXPECT_EQ (r, rr); EXPECT_EQ (g, gg); @@ -109,7 +106,7 @@ TEST (PCL, PointXYZRGBNormal) EXPECT_EQ (bb, 254); p.r = 0; p.g = 127; p.b = 0; - rgb = *reinterpret_cast(&p.rgb); + rgb = p.rgba; rr = (rgb >> 16) & 0x0000ff; gg = (rgb >> 8) & 0x0000ff; bb = (rgb) & 0x0000ff; @@ -413,7 +410,8 @@ TEST (PCL, CopyIfFieldExists) EXPECT_EQ (z_val, 3.0); pcl::for_each_type (CopyIfFieldExists (p, "rgb", is_rgb, rgb_val)); EXPECT_EQ (is_rgb, true); - int rgb = *reinterpret_cast(&rgb_val); + uint32_t rgb; + std::memcpy (&rgb, &rgb_val, sizeof(rgb_val)); EXPECT_EQ (rgb, 0xff7f40fe); // alpha is 255 pcl::for_each_type (CopyIfFieldExists (p, "normal_x", is_normal_x, normal_x_val)); EXPECT_EQ (is_normal_x, true); diff --git a/test/common/test_eigen.cpp b/test/common/test_eigen.cpp index 2aac5c76..4ab723ee 100644 --- a/test/common/test_eigen.cpp +++ b/test/common/test_eigen.cpp @@ -497,25 +497,25 @@ TEST (PCL, eigen22d) // test if U * V * U^T = M r_result = r_vectors * r_eigenvalues.asDiagonal () * r_vectors.transpose (); r_error = r_result - r_matrix; - diff = r_error.cwiseAbs (). sum (); + diff = r_error.cwiseAbs ().maxCoeff (); EXPECT_LE (diff, epsilon); // test if the eigenvalues are orthonormal g_result = r_vectors * r_vectors.transpose (); g_error = g_result - RMatrix::Identity (); - diff = g_error.cwiseAbs (). sum (); + diff = g_error.cwiseAbs ().maxCoeff (); EXPECT_LE (diff, epsilon); // test if column major matrices are also calculated correctly eigen22 (c_matrix, c_vectors, c_eigenvalues); c_result = c_vectors * c_eigenvalues.asDiagonal () * c_vectors.transpose (); c_error = c_result - c_matrix; - diff = c_error.cwiseAbs (). sum (); + diff = c_error.cwiseAbs ().maxCoeff (); EXPECT_LE (diff, epsilon); g_result = c_vectors * c_vectors.transpose (); g_error = g_result - CMatrix::Identity (); - diff = g_error.cwiseAbs (). sum (); + diff = g_error.cwiseAbs ().maxCoeff (); EXPECT_LE (diff, epsilon); } } @@ -556,25 +556,25 @@ TEST (PCL, eigen22f) // test if U * V * U^T = M r_result = r_vectors * r_eigenvalues.asDiagonal () * r_vectors.transpose (); r_error = r_result - r_matrix; - diff = r_error.cwiseAbs (). sum (); + diff = r_error.cwiseAbs ().maxCoeff (); EXPECT_LE (diff, epsilon); // test if the eigenvalues are orthonormal g_result = r_vectors * r_vectors.transpose (); g_error = g_result - RMatrix::Identity (); - diff = g_error.cwiseAbs (). sum (); + diff = g_error.cwiseAbs ().maxCoeff (); EXPECT_LE (diff, epsilon); // test if column major matrices are also calculated correctly eigen22 (c_matrix, c_vectors, c_eigenvalues); c_result = c_vectors * c_eigenvalues.asDiagonal () * c_vectors.transpose (); c_error = c_result - c_matrix; - diff = c_error.cwiseAbs (). sum (); + diff = c_error.cwiseAbs ().maxCoeff (); EXPECT_LE (diff, epsilon); g_result = c_vectors * c_vectors.transpose (); g_error = g_result - CMatrix::Identity (); - diff = g_error.cwiseAbs (). sum (); + diff = g_error.cwiseAbs ().maxCoeff (); EXPECT_LE (diff, epsilon); } } @@ -607,7 +607,7 @@ inline void generateSymPosMatrix3x3 (Matrix& matrix) val2 = val1; val3 = val1; } - // 1%: 2 values are equal but none is set explicitely to 0 + // 1%: 2 values are equal but none is set explicitly to 0 else if (test_case == 1) { val2 = val3; @@ -932,6 +932,8 @@ TEST (PCL, transformPlane) transformationd.linear() = (Eigen::Matrix3d) Eigen::AngleAxisd(M_PI/7, Eigen::Vector3d::UnitY()) * Eigen::AngleAxisd(M_PI/4, Eigen::Vector3d::UnitZ()); test << 5.35315, 2.89914, 0.196848, -49.2788; + test /= test.head<3> ().norm (); + tolerance = 1e-4; plane->values[0] = 5.4; @@ -1108,6 +1110,60 @@ TEST (PCL, transformBetween2CoordinateSystems) EXPECT_LE ((transformationd.matrix())(i,j) - test(i,j), tolerance); } +TEST (PCL, getTransFromUnitVectors) +{ + Eigen::Vector3f xaxis (1, 0, 0), yaxis (0, 1, 0), zaxis (0, 0, 1); + Eigen::Affine3f trans; + + trans = pcl::getTransFromUnitVectorsZY (zaxis, yaxis); + Eigen::Vector3f xaxistrans = trans * xaxis, yaxistrans = trans * yaxis, zaxistrans = trans * zaxis; + EXPECT_NEAR ((xaxistrans - xaxis).norm (), 0.0f, 1e-6); + EXPECT_NEAR ((yaxistrans - yaxis).norm (), 0.0f, 1e-6); + EXPECT_NEAR ((zaxistrans - zaxis).norm (), 0.0f, 1e-6); + + trans = pcl::getTransFromUnitVectorsXY (xaxis, yaxis); + xaxistrans = trans * xaxis, yaxistrans = trans * yaxis, zaxistrans = trans * zaxis; + EXPECT_NEAR ((xaxistrans-xaxis).norm (), 0.0f, 1e-6); + EXPECT_NEAR ((yaxistrans-yaxis).norm (), 0.0f, 1e-6); + EXPECT_NEAR ((zaxistrans-zaxis).norm (), 0.0f, 1e-6); +} + +TEST (PCL, getTransformation) +{ + const float rot_x = 2.8827f; + const float rot_y = -0.31190f; + const float rot_z = -0.93058f; + + Eigen::Affine3f affine; + pcl::getTransformation (0, 0, 0, rot_x, rot_y, rot_z, affine); + + EXPECT_NEAR (affine (0, 0), 0.56854731f, 1e-4); EXPECT_NEAR (affine (0, 1), -0.82217032f, 1e-4); EXPECT_NEAR (affine (0, 2), -0.028107658f, 1e-4); + EXPECT_NEAR (affine (1, 0), -0.76327348f, 1e-4); EXPECT_NEAR (affine (1, 1), -0.51445758f, 1e-4); EXPECT_NEAR (affine (1, 2), -0.39082864f, 1e-4); + EXPECT_NEAR (affine (2, 0), 0.30686751f, 1e-4); EXPECT_NEAR (affine (2, 1), 0.24365838f, 1e-4); EXPECT_NEAR (affine (2, 2), -0.920034f, 1e-4); +} + +TEST (PCL, getTranslationAndEulerAngles) +{ + const float trans_x = 0.1f; + const float trans_y = 0.2f; + const float trans_z = 0.3f; + const float rot_x = 2.8827f; + const float rot_y = -0.31190f; + const float rot_z = -0.93058f; + + Eigen::Affine3f affine; + pcl::getTransformation (trans_x, trans_y, trans_z, rot_x, rot_y, rot_z, affine); + + float tx, ty, tz, rx, ry, rz; + pcl::getTranslationAndEulerAngles (affine, tx, ty, tz, rx, ry, rz); + EXPECT_NEAR (tx, trans_x, 1e-4); + EXPECT_NEAR (ty, trans_y, 1e-4); + EXPECT_NEAR (tz, trans_z, 1e-4); + EXPECT_NEAR (rx, rot_x, 1e-4); + EXPECT_NEAR (ry, rot_y, 1e-4); + EXPECT_NEAR (rz, rot_z, 1e-4); +} + /* ---[ */ int main (int argc, char** argv) diff --git a/test/common/test_io.cpp b/test/common/test_io.cpp index 7bc6f09b..63c0242a 100644 --- a/test/common/test_io.cpp +++ b/test/common/test_io.cpp @@ -87,7 +87,8 @@ TEST (PCL, copyPointCloud) CloudXYZRGBNormal cloud_xyz_rgb_normal; pcl::copyPointCloud (cloud_xyz_rgba, cloud_xyz_rgb_normal); - EXPECT_EQ (int (cloud_xyz_rgb_normal.size ()), 5); + ASSERT_METADATA_EQ (cloud_xyz_rgba, cloud_xyz_rgb_normal); + ASSERT_EQ (int (cloud_xyz_rgb_normal.size ()), 5); for (int i = 0; i < 5; ++i) { EXPECT_XYZ_EQ (cloud_xyz_rgba[i], cloud_xyz_rgb_normal[i]); @@ -98,7 +99,7 @@ TEST (PCL, copyPointCloud) vector indices; indices.push_back (0); indices.push_back (1); pcl::copyPointCloud (cloud_xyz_rgba, indices, cloud_xyz_rgb_normal); - EXPECT_EQ (int (cloud_xyz_rgb_normal.size ()), 2); + ASSERT_EQ (int (cloud_xyz_rgb_normal.size ()), 2); for (int i = 0; i < 2; ++i) { EXPECT_XYZ_EQ (cloud_xyz_rgba[i], cloud_xyz_rgb_normal[i]); @@ -109,7 +110,7 @@ TEST (PCL, copyPointCloud) vector > indices_aligned; indices_aligned.push_back (1); indices_aligned.push_back (2); indices_aligned.push_back (3); pcl::copyPointCloud (cloud_xyz_rgba, indices_aligned, cloud_xyz_rgb_normal); - EXPECT_EQ (int (cloud_xyz_rgb_normal.size ()), 3); + ASSERT_EQ (int (cloud_xyz_rgb_normal.size ()), 3); for (int i = 0; i < 3; ++i) { EXPECT_XYZ_EQ (cloud_xyz_rgba[i], cloud_xyz_rgb_normal[i]); @@ -119,7 +120,7 @@ TEST (PCL, copyPointCloud) PointIndices pindices; pindices.indices.push_back (0); pindices.indices.push_back (2); pindices.indices.push_back (4); - EXPECT_EQ (int (cloud_xyz_rgb_normal.size ()), 3); + ASSERT_EQ (int (cloud_xyz_rgb_normal.size ()), 3); for (int i = 0; i < 3; ++i) { EXPECT_XYZ_EQ (cloud_xyz_rgba[i], cloud_xyz_rgb_normal[i]); diff --git a/test/common/test_transforms.cpp b/test/common/test_transforms.cpp index 43f1331d..6c204b59 100644 --- a/test/common/test_transforms.cpp +++ b/test/common/test_transforms.cpp @@ -1,7 +1,10 @@ /* * Software License Agreement (BSD License) * + * Point Cloud Library (PCL) - www.pointclouds.org * Copyright (c) 2010, Willow Garage, Inc. + * Copyright (c) 2018-, Open Perception, Inc. + * * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,236 +36,175 @@ * * */ -#include -#include // For debug +#include -#include -#include -#include -#include #include #include #include +#include #include using namespace pcl; -using namespace pcl::io; -using namespace std; -const float PI = 3.14159265f; -const float rho = std::sqrt (2.0f) / 2.0f; // cos(PI/4) == sin(PI/4) +typedef ::testing::Types, + Eigen::Transform, + Eigen::Matrix, + Eigen::Matrix > TransformTypes; -PointCloud cloud; -pcl::PCLPointCloud2 cloud_blob; - -void -init () +template +class Transforms : public ::testing::Test { - PointXYZ p0 (0, 0, 0); - PointXYZ p1 (1, 0, 0); - PointXYZ p2 (0, 1, 0); - PointXYZ p3 (0, 0, 1); - cloud.points.push_back (p0); - cloud.points.push_back (p1); - cloud.points.push_back (p2); - cloud.points.push_back (p3); -} + public: + typedef typename Transform::Scalar Scalar; -////////////////////////////////////////////////////////////////////////////////////////////// -TEST (PCL, DeMean) + Transforms () + : ABS_ERROR (std::numeric_limits::epsilon () * 10) + , CLOUD_SIZE (100) + { + Eigen::Matrix r = Eigen::Matrix::Random (); + Eigen::Transform transform; + pcl::getTransformation (r[0], r[1], r[2], r[3], r[4], r[5], transform); + tf = transform.matrix (); + + p_xyz_normal.resize (CLOUD_SIZE); + p_xyz_normal_trans.resize (CLOUD_SIZE); + for (size_t i = 0; i < CLOUD_SIZE; ++i) + { + Eigen::Vector3f xyz = Eigen::Vector3f::Random (); + Eigen::Vector3f normal = Eigen::Vector3f::Random ().normalized (); + p_xyz_normal[i].getVector3fMap () = xyz; + p_xyz_normal_trans[i].getVector3fMap () = (transform * xyz.template cast ()).template cast (); + p_xyz_normal[i].getNormalVector3fMap () = normal; + p_xyz_normal_trans[i].getNormalVector3fMap () = (transform.rotation () * normal.template cast ()).template cast (); + p_xyz_normal[i].rgb = Eigen::Matrix::Random ()[0]; + p_xyz_normal_trans[i].rgb = p_xyz_normal[i].rgb; + } + + pcl::copyPointCloud(p_xyz_normal, p_xyz); + pcl::copyPointCloud(p_xyz_normal_trans, p_xyz_trans); + + indices.resize (CLOUD_SIZE / 2); + for (size_t i = 0; i < indices.size(); ++i) + indices[i] = i * 2; + } + + const Scalar ABS_ERROR; + const size_t CLOUD_SIZE; + + Transform tf; + + // Random point clouds and their expected transformed versions + pcl::PointCloud p_xyz, p_xyz_trans; + pcl::PointCloud p_xyz_normal, p_xyz_normal_trans; + + // Indices, every second point + std::vector indices; + + EIGEN_MAKE_ALIGNED_OPERATOR_NEW; +}; + +TYPED_TEST_CASE (Transforms, TransformTypes); + +TYPED_TEST (Transforms, PointCloudXYZDense) { - PointCloud cloud, cloud_demean; - fromPCLPointCloud2 (cloud_blob, cloud); - - Eigen::Vector4f centroid; - compute3DCentroid (cloud, centroid); - EXPECT_NEAR (centroid[0], -0.0290809, 1e-4); - EXPECT_NEAR (centroid[1], 0.102653, 1e-4); - EXPECT_NEAR (centroid[2], 0.027302, 1e-4); - EXPECT_NEAR (centroid[3], 1, 1e-4); - - // Check standard demean - demeanPointCloud (cloud, centroid, cloud_demean); - EXPECT_EQ (cloud_demean.is_dense, cloud.is_dense); - EXPECT_EQ (cloud_demean.width, cloud.width); - EXPECT_EQ (cloud_demean.height, cloud.height); - EXPECT_EQ (cloud_demean.points.size (), cloud.points.size ()); - - EXPECT_NEAR (cloud_demean.points[0].x, 0.034503, 1e-4); - EXPECT_NEAR (cloud_demean.points[0].y, 0.010837, 1e-4); - EXPECT_NEAR (cloud_demean.points[0].z, 0.013447, 1e-4); - - EXPECT_NEAR (cloud_demean.points[cloud_demean.points.size () - 1].x, -0.048849, 1e-4); - EXPECT_NEAR (cloud_demean.points[cloud_demean.points.size () - 1].y, 0.072507, 1e-4); - EXPECT_NEAR (cloud_demean.points[cloud_demean.points.size () - 1].z, -0.071702, 1e-4); - - vector indices (cloud.points.size ()); - for (int i = 0; i < static_cast (indices.size ()); ++i) { indices[i] = i; } - - // Check standard demean w/ indices - demeanPointCloud (cloud, indices, centroid, cloud_demean); - EXPECT_EQ (cloud_demean.is_dense, cloud.is_dense); - EXPECT_EQ (cloud_demean.width, cloud.width); - EXPECT_EQ (cloud_demean.height, cloud.height); - EXPECT_EQ (cloud_demean.points.size (), cloud.points.size ()); - - EXPECT_NEAR (cloud_demean.points[0].x, 0.034503, 1e-4); - EXPECT_NEAR (cloud_demean.points[0].y, 0.010837, 1e-4); - EXPECT_NEAR (cloud_demean.points[0].z, 0.013447, 1e-4); - - EXPECT_NEAR (cloud_demean.points[cloud_demean.points.size () - 1].x, -0.048849, 1e-4); - EXPECT_NEAR (cloud_demean.points[cloud_demean.points.size () - 1].y, 0.072507, 1e-4); - EXPECT_NEAR (cloud_demean.points[cloud_demean.points.size () - 1].z, -0.071702, 1e-4); - - // Check eigen demean - Eigen::MatrixXf mat_demean; - demeanPointCloud (cloud, centroid, mat_demean); - EXPECT_EQ (mat_demean.cols (), int (cloud.points.size ())); - EXPECT_EQ (mat_demean.rows (), 4); - - EXPECT_NEAR (mat_demean (0, 0), 0.034503, 1e-4); - EXPECT_NEAR (mat_demean (1, 0), 0.010837, 1e-4); - EXPECT_NEAR (mat_demean (2, 0), 0.013447, 1e-4); - - EXPECT_NEAR (mat_demean (0, cloud_demean.points.size () - 1), -0.048849, 1e-4); - EXPECT_NEAR (mat_demean (1, cloud_demean.points.size () - 1), 0.072507, 1e-4); - EXPECT_NEAR (mat_demean (2, cloud_demean.points.size () - 1), -0.071702, 1e-4); - - // Check eigen demean + indices - demeanPointCloud (cloud, indices, centroid, mat_demean); - EXPECT_EQ (mat_demean.cols (), int (cloud.points.size ())); - EXPECT_EQ (mat_demean.rows (), 4); - - EXPECT_NEAR (mat_demean (0, 0), 0.034503, 1e-4); - EXPECT_NEAR (mat_demean (1, 0), 0.010837, 1e-4); - EXPECT_NEAR (mat_demean (2, 0), 0.013447, 1e-4); - - EXPECT_NEAR (mat_demean (0, cloud_demean.points.size () - 1), -0.048849, 1e-4); - EXPECT_NEAR (mat_demean (1, cloud_demean.points.size () - 1), 0.072507, 1e-4); - EXPECT_NEAR (mat_demean (2, cloud_demean.points.size () - 1), -0.071702, 1e-4); + pcl::PointCloud p; + pcl::transformPointCloud (this->p_xyz, p, this->tf); + ASSERT_METADATA_EQ (p, this->p_xyz); + ASSERT_EQ (p.size (), this->p_xyz.size ()); + for (size_t i = 0; i < p.size (); ++i) + ASSERT_XYZ_NEAR (p[i], this->p_xyz_trans[i], this->ABS_ERROR); } -////////////////////////////////////////////////////////////////////////////////////////////// -TEST (PCL, Transform) +TYPED_TEST (Transforms, PointCloudXYZDenseIndexed) { - Eigen::Vector3f offset (100, 0, 0); - float angle = PI/4; - Eigen::Quaternionf rotation (cos (angle / 2), 0, 0, sin (angle / 2)); - - PointCloud cloud_out; - const vector > &points (cloud_out.points); - transformPointCloud (cloud, cloud_out, offset, rotation); - - EXPECT_EQ (cloud.points.size (), cloud_out.points.size ()); - EXPECT_EQ (100, points[0].x); - EXPECT_EQ (0, points[0].y); - EXPECT_EQ (0, points[0].z); - EXPECT_NEAR (100+rho, points[1].x, 1e-4); - EXPECT_NEAR (rho, points[1].y, 1e-4); - EXPECT_EQ (0, points[1].z); - EXPECT_NEAR (100-rho, points[2].x, 1e-4); - EXPECT_NEAR (rho, points[2].y, 1e-4); - EXPECT_EQ (0, points[2].z); - EXPECT_EQ (100, points[3].x); - EXPECT_EQ (0, points[3].y); - EXPECT_EQ (1, points[3].z); - - PointCloud cloud_out2; - const vector > &points2 (cloud_out2.points); - Eigen::Translation3f translation (offset); - Eigen::Affine3f transform; - transform = translation * rotation; - transformPointCloud (cloud, cloud_out2, transform); - - EXPECT_EQ (cloud.points.size (), cloud_out2.points.size ()); - EXPECT_EQ (100, points2[0].x); - EXPECT_EQ (0, points2[0].y); - EXPECT_EQ (0, points2[0].z); - EXPECT_NEAR (100+rho, points2[1].x, 1e-4); - EXPECT_NEAR (rho, points2[1].y, 1e-4); - EXPECT_EQ (0, points2[1].z); - EXPECT_NEAR (100-rho, points2[2].x, 1e-4); - EXPECT_NEAR (rho, points2[2].y, 1e-4); - EXPECT_EQ (0, points2[2].z); - EXPECT_EQ (100, points2[3].x); - EXPECT_EQ (0, points2[3].y); - EXPECT_EQ (1, points2[3].z); + pcl::PointCloud p; + pcl::transformPointCloud (this->p_xyz, this->indices, p, this->tf); + ASSERT_EQ (p.size (), this->indices.size ()); + ASSERT_EQ (p.width, this->indices.size ()); + ASSERT_EQ (p.height, 1); + for (size_t i = 0; i < p.size (); ++i) + ASSERT_XYZ_NEAR (p[i], this->p_xyz_trans[i * 2], this->ABS_ERROR); } -////////////////////////////////////////////////////////////////////////////////////////////// -TEST (PCL, TransformCopyFields) +TYPED_TEST (Transforms, PointCloudXYZSparse) { - Eigen::Affine3f transform; - transform = Eigen::Translation3f (100, 0, 0); - - PointXYZRGBNormal empty_point; - std::vector indices (1); - - PointCloud cloud (2, 1); - cloud.points[0].rgba = 0xFF0000; - cloud.points[1].rgba = 0x00FF00; - - // Preserve data in all fields + // Make first point infinite and point cloud not dense + this->p_xyz.is_dense = false; + this->p_xyz[0].x = std::numeric_limits::quiet_NaN(); + + pcl::PointCloud p; + pcl::transformPointCloud (this->p_xyz, p, this->tf); + ASSERT_METADATA_EQ (p, this->p_xyz); + ASSERT_EQ (p.size (), this->p_xyz.size ()); + ASSERT_FALSE (pcl::isFinite (p[0])); + for (size_t i = 1; i < p.size (); ++i) { - PointCloud cloud_out; - transformPointCloud (cloud, cloud_out, transform, true); - ASSERT_EQ (cloud.size (), cloud_out.size ()); - EXPECT_RGBA_EQ (cloud.points[0], cloud_out.points[0]); - EXPECT_RGBA_EQ (cloud.points[1], cloud_out.points[1]); + ASSERT_TRUE (pcl::isFinite (p[i])); + ASSERT_XYZ_NEAR (p[i], this->p_xyz_trans[i], this->ABS_ERROR); } - // Preserve data in all fields (with indices) - { - PointCloud cloud_out; - transformPointCloud (cloud, indices, cloud_out, transform, true); - ASSERT_EQ (indices.size (), cloud_out.size ()); - EXPECT_RGBA_EQ (cloud.points[0], cloud_out.points[0]); - } - // Do not preserve data in all fields - { - PointCloud cloud_out; - transformPointCloud (cloud, cloud_out, transform, false); - ASSERT_EQ (cloud.size (), cloud_out.size ()); - EXPECT_RGBA_EQ (empty_point, cloud_out.points[0]); - EXPECT_RGBA_EQ (empty_point, cloud_out.points[1]); - } - // Do not preserve data in all fields (with indices) - { - PointCloud cloud_out; - transformPointCloud (cloud, indices, cloud_out, transform, false); - ASSERT_EQ (indices.size (), cloud_out.size ()); - EXPECT_RGBA_EQ (empty_point, cloud_out.points[0]); - } - // Preserve data in all fields (with normals version) +} + +TYPED_TEST (Transforms, PointCloudXYZRGBNormalDense) +{ + // Copy all fields { - PointCloud cloud_out; - transformPointCloudWithNormals (cloud, cloud_out, transform, true); - ASSERT_EQ (cloud.size (), cloud_out.size ()); - EXPECT_RGBA_EQ (cloud.points[0], cloud_out.points[0]); - EXPECT_RGBA_EQ (cloud.points[1], cloud_out.points[1]); + pcl::PointCloud p; + pcl::transformPointCloudWithNormals (this->p_xyz_normal, p, this->tf, true); + ASSERT_METADATA_EQ (p, this->p_xyz_normal); + ASSERT_EQ (p.size (), this->p_xyz_normal.size ()); + for (size_t i = 0; i < p.size (); ++i) + { + ASSERT_XYZ_NEAR (p[i], this->p_xyz_normal_trans[i], this->ABS_ERROR); + ASSERT_NORMAL_NEAR (p[i], this->p_xyz_normal_trans[i], this->ABS_ERROR); + ASSERT_RGBA_EQ (p[i], this->p_xyz_normal_trans[i]); + } } - // Preserve data in all fields (with normals and indices version) + // Do not copy all fields { - PointCloud cloud_out; - transformPointCloudWithNormals (cloud, indices, cloud_out, transform, true); - ASSERT_EQ (indices.size (), cloud_out.size ()); - EXPECT_RGBA_EQ (cloud.points[0], cloud_out.points[0]); + pcl::PointCloud p; + pcl::transformPointCloudWithNormals (this->p_xyz_normal, p, this->tf, false); + ASSERT_METADATA_EQ (p, this->p_xyz_normal); + ASSERT_EQ (p.size (), this->p_xyz_normal.size ()); + for (size_t i = 0; i < p.size (); ++i) + { + ASSERT_XYZ_NEAR (p[i], this->p_xyz_normal_trans[i], this->ABS_ERROR); + ASSERT_NORMAL_NEAR (p[i], this->p_xyz_normal_trans[i], this->ABS_ERROR); + ASSERT_NE (p[i].rgba, this->p_xyz_normal_trans[i].rgba); + } } - // Do not preserve data in all fields (with normals version) +} + +TYPED_TEST (Transforms, PointCloudXYZRGBNormalDenseIndexed) +{ + // Copy all fields { - PointCloud cloud_out; - transformPointCloudWithNormals (cloud, cloud_out, transform, false); - ASSERT_EQ (cloud.size (), cloud_out.size ()); - EXPECT_RGBA_EQ (empty_point, cloud_out.points[0]); - EXPECT_RGBA_EQ (empty_point, cloud_out.points[1]); + pcl::PointCloud p; + pcl::transformPointCloudWithNormals (this->p_xyz_normal, this->indices, p, this->tf, true); + ASSERT_EQ (p.size (), this->indices.size ()); + ASSERT_EQ (p.width, this->indices.size ()); + ASSERT_EQ (p.height, 1); + for (size_t i = 0; i < p.size (); ++i) + { + ASSERT_XYZ_NEAR (p[i], this->p_xyz_normal_trans[i * 2], this->ABS_ERROR); + ASSERT_NORMAL_NEAR (p[i], this->p_xyz_normal_trans[i * 2], this->ABS_ERROR); + ASSERT_RGBA_EQ (p[i], this->p_xyz_normal_trans[i * 2]); + } } - // Do not preserve data in all fields (with normals and indices version) + // Do not copy all fields { - PointCloud cloud_out; - transformPointCloudWithNormals (cloud, indices, cloud_out, transform, false); - ASSERT_EQ (indices.size (), cloud_out.size ()); - EXPECT_RGBA_EQ (empty_point, cloud_out.points[0]); + pcl::PointCloud p; + pcl::transformPointCloudWithNormals (this->p_xyz_normal, this->indices, p, this->tf, false); + ASSERT_EQ (p.size (), this->indices.size ()); + ASSERT_EQ (p.width, this->indices.size ()); + ASSERT_EQ (p.height, 1); + for (size_t i = 0; i < p.size (); ++i) + { + ASSERT_XYZ_NEAR (p[i], this->p_xyz_normal_trans[i * 2], this->ABS_ERROR); + ASSERT_NORMAL_NEAR (p[i], this->p_xyz_normal_trans[i * 2], this->ABS_ERROR); + ASSERT_NE (p[i].rgba, this->p_xyz_normal_trans[i * 2].rgba); + } } } @@ -273,32 +215,8 @@ TEST (PCL, Matrix4Affine3Transform) float rot_y = -0.31190f; float rot_z = -0.93058f; Eigen::Affine3f affine; - pcl::getTransformation (0, 0, 0, rot_x, rot_y, rot_z, affine); - - EXPECT_NEAR (affine (0, 0), 0.56854731f, 1e-4); EXPECT_NEAR (affine (0, 1), -0.82217032f, 1e-4); EXPECT_NEAR (affine (0, 2), -0.028107658f, 1e-4); - EXPECT_NEAR (affine (1, 0), -0.76327348f, 1e-4); EXPECT_NEAR (affine (1, 1), -0.51445758f, 1e-4); EXPECT_NEAR (affine (1, 2), -0.39082864f, 1e-4); - EXPECT_NEAR (affine (2, 0), 0.30686751f, 1e-4); EXPECT_NEAR (affine (2, 1), 0.24365838f, 1e-4); EXPECT_NEAR (affine (2, 2), -0.920034f, 1e-4); - - // Approximative!!! Uses SVD internally! See http://eigen.tuxfamily.org/dox/Transform_8h_source.html - Eigen::Matrix3f rotation = affine.rotation (); - - EXPECT_NEAR (rotation (0, 0), 0.56854731f, 1e-4); EXPECT_NEAR (rotation (0, 1), -0.82217032f, 1e-4); EXPECT_NEAR (rotation (0, 2), -0.028107658f, 1e-4); - EXPECT_NEAR (rotation (1, 0), -0.76327348f, 1e-4); EXPECT_NEAR (rotation (1, 1), -0.51445758f, 1e-4); EXPECT_NEAR (rotation (1, 2), -0.39082864f, 1e-4); - EXPECT_NEAR (rotation (2, 0), 0.30686751f, 1e-4); EXPECT_NEAR (rotation (2, 1), 0.24365838f, 1e-4); EXPECT_NEAR (rotation (2, 2), -0.920034f, 1e-4); - - float trans_x, trans_y, trans_z; pcl::getTransformation (0.1f, 0.2f, 0.3f, rot_x, rot_y, rot_z, affine); - pcl::getTranslationAndEulerAngles (affine, trans_x, trans_y, trans_z, rot_x, rot_y, rot_z); - EXPECT_FLOAT_EQ (trans_x, 0.1f); - EXPECT_FLOAT_EQ (trans_y, 0.2f); - EXPECT_FLOAT_EQ (trans_z, 0.3f); - EXPECT_FLOAT_EQ (rot_x, 2.8827f); - EXPECT_FLOAT_EQ (rot_y, -0.31190f); - EXPECT_FLOAT_EQ (rot_z, -0.93058f); - - Eigen::Matrix4f transformation (Eigen::Matrix4f::Identity ()); - transformation.block<3, 3> (0, 0) = affine.rotation (); - transformation.block<3, 1> (0, 3) = affine.translation (); + Eigen::Matrix4f transformation = affine.matrix (); PointXYZ p (1.f, 2.f, 3.f); Eigen::Vector3f v3 = p.getVector3fMap (); @@ -358,42 +276,11 @@ TEST (PCL, Matrix4Affine3Transform) EXPECT_NEAR (pt.z, ct[0].z, 1e-4); } -////////////////////////////////////////////////////////////////////////////////////////////// -TEST (PCL, commonTransform) -{ - Eigen::Vector3f xaxis (1,0,0), yaxis (0,1,0), zaxis (0,0,1); - Eigen::Affine3f trans = pcl::getTransFromUnitVectorsZY (zaxis, yaxis); - Eigen::Vector3f xaxistrans=trans*xaxis, yaxistrans=trans*yaxis, zaxistrans=trans*zaxis; - //std::cout << xaxistrans<<"\n"< cloud2 (5, 80, nan_point); EXPECT_EQ (cloud2.width, 5); EXPECT_EQ (cloud2.height, 80); EXPECT_EQ (cloud2.size (), 5*80); - for (PointCloud::const_iterator pit = cloud2.begin (); - pit != cloud2.end (); - ++pit) + for (PointCloud::const_iterator pit = cloud2.begin (); pit != cloud2.end (); ++pit) { EXPECT_NEAR (pit->x, 0.1, 1e-3); EXPECT_NEAR (pit->y, 0.2, 1e-3); EXPECT_NEAR (pit->z, 0.3, 1e-3); } - } TEST (PointCloud, iterators) { - EXPECT_EQ_VECTORS (cloud.begin ()->getVector3fMap (), + EXPECT_EQ_VECTORS (cloud.begin ()->getVector3fMap (), cloud.points.begin ()->getVector3fMap ()); - EXPECT_EQ_VECTORS (cloud.end ()->getVector3fMap (), - cloud.points.end ()->getVector3fMap ()); + EXPECT_EQ_VECTORS ((--cloud.end ())->getVector3fMap (), + (--cloud.points.end ())->getVector3fMap ()); PointCloud::const_iterator pit = cloud.begin (); PointCloud::VectorType::const_iterator pit2 = cloud.points.begin (); for (; pit < cloud.end (); ++pit2, ++pit) diff --git a/test/features/CMakeLists.txt b/test/features/CMakeLists.txt index afa18a5f..cedb0cb4 100644 --- a/test/features/CMakeLists.txt +++ b/test/features/CMakeLists.txt @@ -78,6 +78,10 @@ if (build) FILES test_board_estimation.cpp LINK_WITH pcl_gtest pcl_features pcl_io ARGUMENTS "${PCL_SOURCE_DIR}/test/bun0.pcd") + PCL_ADD_TEST(feature_flare_estimation test_flare_estimation + FILES test_flare_estimation.cpp + LINK_WITH pcl_gtest pcl_features pcl_io + ARGUMENTS "${PCL_SOURCE_DIR}/test/bun0.pcd") PCL_ADD_TEST(feature_shot_lrf_estimation test_shot_lrf_estimation FILES test_shot_lrf_estimation.cpp LINK_WITH pcl_gtest pcl_features pcl_io @@ -97,6 +101,10 @@ if (build) FILES test_rops_estimation.cpp LINK_WITH pcl_gtest pcl_io pcl_features ARGUMENTS "${PCL_SOURCE_DIR}/test/rops_cloud.pcd" "${PCL_SOURCE_DIR}/test/rops_indices.txt" "${PCL_SOURCE_DIR}/test/rops_triangles.txt") + PCL_ADD_TEST(feature_gasd_estimation test_gasd_estimation + FILES test_gasd_estimation.cpp + LINK_WITH pcl_gtest pcl_io pcl_features + ARGUMENTS "${PCL_SOURCE_DIR}/test/bun0.pcd") if (BUILD_keypoints) PCL_ADD_TEST(feature_brisk test_brisk FILES test_brisk.cpp @@ -105,4 +113,3 @@ if (build) endif (BUILD_keypoints) endif (BUILD_io) endif (build) - diff --git a/test/features/test_cppf_estimation.cpp b/test/features/test_cppf_estimation.cpp index cc2ab264..8d7e1335 100755 --- a/test/features/test_cppf_estimation.cpp +++ b/test/features/test_cppf_estimation.cpp @@ -39,41 +39,24 @@ #include #include -#include #include #include -using namespace pcl; -using namespace pcl::io; -using namespace std; +typedef pcl::PointXYZRGBNormal PointT; +static pcl::PointCloud::Ptr cloud (new pcl::PointCloud ()); -typedef search::KdTree::Ptr KdTreePtr; - -PointCloud cloud; -vector indices; -KdTreePtr tree; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TEST (PCL, CPPFEstimation) { - // Estimate normals - NormalEstimation normal_estimation; - PointCloud::Ptr normals (new PointCloud ()); - normal_estimation.setInputCloud (cloud.makeShared ()); - boost::shared_ptr > indicesptr (new vector (indices)); - normal_estimation.setIndices (indicesptr); - normal_estimation.setSearchMethod (tree); - normal_estimation.setKSearch (10); // Use 10 nearest neighbors to estimate the normals - normal_estimation.compute (*normals); - - CPPFEstimation cppf_estimation; - cppf_estimation.setInputCloud (cloud.makeShared ()); - cppf_estimation.setInputNormals (normals); - PointCloud::Ptr feature_cloud (new PointCloud ()); + pcl::CPPFEstimation cppf_estimation; + cppf_estimation.setInputCloud (cloud); + cppf_estimation.setInputNormals (cloud); + pcl::PointCloud::Ptr feature_cloud (new pcl::PointCloud ()); cppf_estimation.compute (*feature_cloud); // Check for size of output - EXPECT_EQ (feature_cloud->points.size (), indices.size () * cloud.points.size ()); + EXPECT_EQ (feature_cloud->size (), cloud->size () * cloud->size ()); // Now check for a few values in the feature cloud EXPECT_TRUE (pcl_isnan (feature_cloud->points[0].f1)); @@ -88,9 +71,9 @@ TEST (PCL, CPPFEstimation) EXPECT_TRUE (pcl_isnan (feature_cloud->points[0].f10)); EXPECT_TRUE (pcl_isnan (feature_cloud->points[0].alpha_m)); - EXPECT_NEAR (feature_cloud->points[2572].f1, 0.0669282, 1e-4); + EXPECT_NEAR (feature_cloud->points[2572].f1, 0.0568356, 1e-4); EXPECT_NEAR (feature_cloud->points[2572].f2, -0.1988939, 1e-4); - EXPECT_NEAR (feature_cloud->points[2572].f3, 0.7856042, 1e-4); + EXPECT_NEAR (feature_cloud->points[2572].f3, 0.7854938, 1e-4); EXPECT_NEAR (feature_cloud->points[2572].f4, 0.0533117, 1e-4); EXPECT_NEAR (feature_cloud->points[2572].f5, 0.1875000, 1e-4); EXPECT_NEAR (feature_cloud->points[2572].f6, 0.0733944, 1e-4); @@ -98,8 +81,7 @@ TEST (PCL, CPPFEstimation) EXPECT_NEAR (feature_cloud->points[2572].f8, 0.2380952, 1e-4); EXPECT_NEAR (feature_cloud->points[2572].f9, 0.0619469, 1e-4); EXPECT_NEAR (feature_cloud->points[2572].f10, 0.4431372, 1e-4); - EXPECT_NEAR (feature_cloud->points[2572].alpha_m, -1.852997, 1e-4); - + EXPECT_NEAR (feature_cloud->points[2572].alpha_m, -1.847514, 1e-4); } /* ---[ */ @@ -109,22 +91,15 @@ main (int argc, char** argv) if (argc < 2) { std::cerr << "No test file given. Please download `colored_cloud.pcd` and pass its path to the test." << std::endl; - return (-1); + return (1); } - if (loadPCDFile (argv[1], cloud) < 0) + if (pcl::io::loadPCDFile (argv[1], *cloud) < 0) { std::cerr << "Failed to read test file. Please download `colored_cloud.pcd` and pass its path to the test." << std::endl; - return (-1); + return (1); } - indices.resize (cloud.points.size ()); - for (int i = 0; i < static_cast (indices.size ()); ++i) - indices[i] = i; - - tree.reset (new search::KdTree (false)); - tree->setInputCloud (cloud.makeShared ()); - testing::InitGoogleTest (&argc, argv); return (RUN_ALL_TESTS ()); } diff --git a/test/features/test_flare_estimation.cpp b/test/features/test_flare_estimation.cpp new file mode 100644 index 00000000..0cfdcfeb --- /dev/null +++ b/test/features/test_flare_estimation.cpp @@ -0,0 +1,179 @@ +/* +* Software License Agreement (BSD License) +* +* Point Cloud Library (PCL) - www.pointclouds.org +* Copyright (c) 2016-, Open Perception, Inc. +* +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided +* with the distribution. +* * Neither the name of the copyright holder(s) nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +* $Id$ +* +*/ + +#include +#include +#include +#include +#include +#include + +typedef pcl::search::KdTree::Ptr KdTreePtr; +typedef pcl::PointCloud::Ptr PointCloudPtr; + +PointCloudPtr cloud; +KdTreePtr tree; + +//sampled surface for the computation of tangent X axis +PointCloudPtr sampled_cloud; +KdTreePtr sampled_tree; + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +TEST (PCL, FLARELocalReferenceFrameEstimation) +{ + pcl::PointCloud::Ptr normals (new pcl::PointCloud ()); + pcl::PointCloud bunny_LRF; + + const float mesh_res = 0.005f; + + // Compute normals + pcl::NormalEstimation ne; + + ne.setRadiusSearch (2.0f*mesh_res); + ne.setViewPoint (1, 1, 10); + ne.setInputCloud (cloud); + ne.setSearchMethod (tree); + + ne.compute (*normals); + + // Compute FLARE LRF + pcl::FLARELocalReferenceFrameEstimation lrf_estimator; + + lrf_estimator.setRadiusSearch (5 * mesh_res); + lrf_estimator.setTangentRadius (20 * mesh_res); + + lrf_estimator.setInputCloud (cloud); + lrf_estimator.setSearchSurface (cloud); + lrf_estimator.setInputNormals (normals); + lrf_estimator.setSearchMethod (tree); + lrf_estimator.setSearchMethodForSampledSurface (sampled_tree); + lrf_estimator.setSearchSampledSurface (sampled_cloud); + + lrf_estimator.compute (bunny_LRF); + + // TESTS + EXPECT_TRUE (bunny_LRF.is_dense); + + // Expected Results + float score_15 = -0.0059431493f; + Eigen::Vector3f point_15_x (-0.46138301f, 0.75752199f, -0.46182927f); + Eigen::Vector3f point_15_y (-0.78785944f, -0.11049186f, 0.60586232f); + Eigen::Vector3f point_15_z (0.40792558f, 0.64339107f, 0.64779979f); + float score_45 = 0.018918669f; + Eigen::Vector3f point_45_x (0.63724411f, -0.74846953f, -0.18361199f); + Eigen::Vector3f point_45_y (0.76468521f, 0.58447874f, 0.27136898f); + Eigen::Vector3f point_45_z (-0.095794097f, -0.31333363f, 0.94479918f); + float score_163 = -0.050190225f; + Eigen::Vector3f point_163_x (-0.67064381f, 0.45722002f, 0.58411193f); + Eigen::Vector3f point_163_y (-0.58332449f, -0.81150508f, -0.034525186f); + Eigen::Vector3f point_163_z (0.45822418f, -0.36388087f, 0.81093854f); + float score_253 = -0.025943652f; + Eigen::Vector3f point_253_x (0.88240892f, -0.26585102f, 0.38817233f); + Eigen::Vector3f point_253_y (0.19853911f, 0.95840079f, 0.20506138f); + Eigen::Vector3f point_253_z (-0.42654046f, -0.10388060f, 0.89848322f); + + + //Test Results + for (int d = 0; d < 3; ++d) + { + EXPECT_NEAR (point_15_x[d], bunny_LRF.at (15).x_axis[d], 1E-3); + EXPECT_NEAR (point_15_y[d], bunny_LRF.at (15).y_axis[d], 1E-3); + EXPECT_NEAR (point_15_z[d], bunny_LRF.at (15).z_axis[d], 1E-3); + + EXPECT_NEAR (point_45_x[d], bunny_LRF.at (45).x_axis[d], 1E-3); + EXPECT_NEAR (point_45_y[d], bunny_LRF.at (45).y_axis[d], 1E-3); + EXPECT_NEAR (point_45_z[d], bunny_LRF.at (45).z_axis[d], 1E-3); + + EXPECT_NEAR (point_163_x[d], bunny_LRF.at (163).x_axis[d], 1E-3); + EXPECT_NEAR (point_163_y[d], bunny_LRF.at (163).y_axis[d], 1E-3); + EXPECT_NEAR (point_163_z[d], bunny_LRF.at (163).z_axis[d], 1E-3); + + EXPECT_NEAR (point_253_x[d], bunny_LRF.at (253).x_axis[d], 1E-3); + EXPECT_NEAR (point_253_y[d], bunny_LRF.at (253).y_axis[d], 1E-3); + EXPECT_NEAR (point_253_z[d], bunny_LRF.at (253).z_axis[d], 1E-3); + } + EXPECT_NEAR (score_15, lrf_estimator.getSignedDistancesFromHighestPoints ()[15], 1E-4); + EXPECT_NEAR (score_45, lrf_estimator.getSignedDistancesFromHighestPoints ()[45], 1E-4); + EXPECT_NEAR (score_163, lrf_estimator.getSignedDistancesFromHighestPoints ()[163], 1E-4); + EXPECT_NEAR (score_253, lrf_estimator.getSignedDistancesFromHighestPoints ()[253], 1E-4); +} + + +/* ---[ */ +int +main (int argc, char** argv) +{ + if (argc < 2) + { + std::cerr << "No test file given. Please download `bun0.pcd` and pass its path to the test." << std::endl; + return (-1); + } + + cloud.reset (new pcl::PointCloud ()); + + if (pcl::io::loadPCDFile (argv[1], *cloud) < 0) + { + std::cerr << "Failed to read test file. Please download `bun0.pcd` and pass its path to the test." << std::endl; + return (-1); + } + + tree.reset (new pcl::search::KdTree (false)); + tree->setInputCloud (cloud); + + //create and set sampled point cloud for computation of X axis + const float sampling_perc = 0.2f; + const float sampling_incr = 1.0f / sampling_perc; + + sampled_cloud.reset (new pcl::PointCloud ()); + + std::vector sampled_indices; + for (float sa = 0.0f; sa < (float)cloud->points.size (); sa += sampling_incr) + sampled_indices.push_back (static_cast (sa)); + copyPointCloud (*cloud, sampled_indices, *sampled_cloud); + + sampled_tree.reset (new pcl::search::KdTree (false)); + sampled_tree->setInputCloud (sampled_cloud); + + //start tests + testing::InitGoogleTest (&argc, argv); + return (RUN_ALL_TESTS ()); +} +/* ]--- */ diff --git a/test/features/test_gasd_estimation.cpp b/test/features/test_gasd_estimation.cpp new file mode 100644 index 00000000..f6f1cd53 --- /dev/null +++ b/test/features/test_gasd_estimation.cpp @@ -0,0 +1,335 @@ +/* + * Software License Agreement (BSD License) + * + * Point Cloud Library (PCL) - www.pointclouds.org + * Copyright (c) 2016-, Open Perception, Inc. + * Copyright (c) 2016, Voxar Labs, CIn-UFPE / DEINFO-UFRPE + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the copyright holder(s) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include + +pcl::PointCloud::Ptr cloud; + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void +createColorCloud (pcl::PointCloud &colorCloud) +{ + for (size_t i = 0; i < cloud->points.size (); ++i) + { + pcl::PointXYZRGBA p; + p.getVector3fMap () = cloud->points[i].getVector3fMap (); + + p.rgba = ( (i % 255) << 16) + ( ( (255 - i) % 255) << 8) + ( (i * 37) % 255); + colorCloud.push_back (p); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +TEST(PCL, GASDTransformEstimation) +{ + pcl::GASDEstimation gasd; + gasd.setInputCloud (cloud); + + pcl::PointCloud descriptor; + gasd.compute (descriptor); + + Eigen::Matrix4f trans = gasd.getTransform (); + + Eigen::Matrix4f ref_trans; + ref_trans << 0.661875, -0.704840, 0.255192, 0.0846344, + -0.748769, -0.605475, 0.269713, 0.0330151, + -0.035592, -0.369596, -0.928511, 0.0622551, + 0, 0, 0, 1; + + for (int i = 0; i < trans.rows(); ++i) + { + for (int j = 0; j < trans.cols (); ++j) + { + EXPECT_NEAR (trans (i, j), ref_trans (i, j), 1e-5); + } + } +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +TEST (PCL, GASDShapeEstimationNoInterp) +{ + pcl::GASDEstimation gasd; + gasd.setInputCloud (cloud); + gasd.setShapeHistsInterpMethod (pcl::INTERP_NONE); + + pcl::PointCloud descriptor; + gasd.compute (descriptor); + + const float ref_values[512] = + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2.0202, 0, 0, 0, 0, 0, 0, 1.76768, 1.26263, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 2.77778, 0, 0, 0, 0, 0, 0, 1.0101, 2.0202, 0, 0, 0, 0, 0, 0, 2.0202, 0.757576, 0, 0, + 0, 0, 0, 1.0101, 3.28283, 0, 0, 0, 0, 0, 0, 0, 1.51515, 0.252525, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.51515, 0, 0, 0, 0, 0, 0, 0, 1.51515, 0, 0, 0, 0, 0, 0, + 2.27273, 0.757576, 0, 0, 0, 0, 0, 2.0202, 2.0202, 0, 0, 0, 0, 0, 0, 1.0101, 2.27273, 2.27273, 0, 0, 0, 0, 0, + 0, 0, 0.252525, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1.0101, 0, 0, 0, 0, 0, 0, 0, 2.77778, 0.252525, 0, 0, 0, 0, 0, 0, 2.77778, 0, 0, 0, 0, 0, 0, 0, 2.0202, + 1.0101, 0.252525, 0, 0, 0, 0, 0, 0, 2.0202, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0.757576, 0, 0, 0, 0, 0, 0, 0, 3.0303, 0, 0, 0, 0, 0, 0, 0, 2.77778, 0, 0, 0, 0, 0, 0, 0, 2.52525, 0, 0, 0, 0, + 0, 0, 0, 1.0101, 4.29293, 1.26263, 0, 0, 0, 0, 0, 0, 1.0101, 1.76768, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0.757576, 1.26263, 0, 0, 0, 0, 0, 0, 2.77778, 0.252525, 0, 0, 0, 0, 0, 0, 2.52525, 0, + 0, 0, 0, 0, 0, 0, 2.77778, 0.252525, 0, 0, 0, 0, 0, 0, 0.50505, 3.53535, 0, 0, 0, 0, 0, 0, 0, 1.0101, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.51515, 3.53535, 0, 0, 0, 0, + 0, 0, 2.0202, 1.51515, 0, 0, 0, 0, 0, 0, 1.76768, 1.26263, 0, 0, 0, 0, 0, 0, 0.757576, 0.757576, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2.0202, 0.252525, 0, 0, 0, 0, 0, 0, 2.52525, 0, 0, 0, 0, 0, 0, 0, 0.50505, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + EXPECT_EQ (descriptor.points.size (), 1); + for (size_t i = 0; i < size_t (descriptor.points[0].descriptorSize ()); ++i) + { + EXPECT_NEAR (descriptor.points[0].histogram[i], ref_values[i], 1e-5); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +TEST(PCL, GASDShapeEstimationTrilinearInterp) +{ + pcl::GASDEstimation gasd; + gasd.setInputCloud (cloud); + + pcl::PointCloud descriptor; + gasd.compute (descriptor); + + const float ref_values[512] = + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.00836128, 0.00588401, 0, 0, 0, 0, 0, 0.107094, 0.662119, 0.175875, 0, + 0, 0, 0, 0.00971758, 0.839464, 0.58815, 0.0212094, 0, 0, 0, 0, 0.231226, 0.0734101, 0.0136519, 0, 0, 0, 0, 0, + 0.0148243, 0.00481199, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0175941, + 0.00421349, 0, 0, 0, 0, 0, 0.283015, 1.10961, 0.0363255, 0, 0, 0, 0, 0, 0.330514, 0.765326, 0.0400337, 0, 0, + 0, 0, 0.113547, 0.602405, 0.760736, 0.162908, 0, 0, 0, 0, 0.19996, 0.568878, 0.0862251, 0.00529312, 0, 0, 0, + 0, 0.029714, 0.0622333, 0.0943459, 0, 0, 0, 0, 0, 0, 0.000478281, 0.0027764, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0.0274657, 0.00816464, 0, 0, 0, 0, 0, 0.0368769, 0.609568, 0.0643177, 0, 0, 0, 0, 0, 0.231449, + 0.577855, 0.00143416, 0, 0, 0, 0, 0.150154, 1.03347, 0.499773, 0, 0, 0, 0, 0, 0.856571, 1.09567, 0.0753022, 0, + 0, 0, 0, 0, 0.589158, 1.39603, 1.21675, 0.000193459, 0, 0, 0, 0, 0, 0.0955702, 0.30635, 0.000289646, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.030262, 0.0172037, 0, 0, 0, 0, 0, 0.00457827, + 0.375081, 0.115732, 0, 0, 0, 0, 0, 0.0289331, 1.70761, 0.498999, 0, 0, 0, 0, 0, 0.0748183, 1.59429, 0.1418, 0, + 0, 0, 0, 0, 0.0890234, 1.12343, 1.32057, 0.192363, 2.03302e-005, 0, 0, 0, 0, 0.310746, 1.158, 0.215904, + 0.00137463, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.000985736, 0.525969, 0.173892, 0, 0, 0, + 0, 0, 0.03184, 1.10013, 0.177279, 0, 0, 0, 0, 0, 0.157355, 1.27302, 0.0196858, 0, 0, 0, 0, 0, 0.0968957, + 1.44605, 0.111663, 0, 0, 0, 0, 0, 0.00114074, 0.414239, 1.5723, 0.589681, 0.000551233, 0, 0, 0, 0, 0.0759757, + 0.837368, 0.721145, 0.0346183, 0, 0, 0, 0, 0, 0.0230347, 0.00829717, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0.000667462, 0.265466, 0.211503, 0.0281879, 0, 0, 0, 0, 0.130565, 1.38486, 0.168072, 0.020685, 0, 0, 0, 0, + 0.242921, 0.93304, 0, 0, 0, 0, 0, 0, 0.0796441, 1.00082, 0.289627, 0, 0, 0, 0, 0, 0.000704473, 0.596791, + 1.22236, 0.015159, 1.08117e-005, 0, 0, 0, 0, 0.0205081, 0.214601, 0.0495967, 0.000464472, 0, 0, 0, 0, 0, + 0.0129949, 0.0046808, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.00173052, 0.319008, 0.0513479, 0, 0, 0, 0, + 0.0156562, 0.232644, 1.43675, 0.106155, 0, 0, 0, 0, 0.0809344, 0.605434, 0.803232, 0.0557288, 0, 0, 0, 0, + 0.0272915, 0.607169, 0.366215, 0.0111251, 0, 0, 0, 0, 4.34266e-005, 0.141203, 0.0975747, 0, 0, 0, 0, 0, 0, + 0.000786102, 0.00152033, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.000134866, + 0.0595355, 0.00785339, 0, 0, 0, 0, 0, 0.0386555, 0.582852, 0.0295824, 0, 0, 0, 0, 0, 0.118361, 1.33305, + 0.209958, 0, 0, 0, 0, 0, 0.0603114, 0.412731, 0.0294292, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0}; + + EXPECT_EQ (descriptor.points.size (), 1); + for (size_t i = 0; i < size_t (descriptor.points[0].descriptorSize ()); ++i) + { + EXPECT_NEAR (descriptor.points[0].histogram[i], ref_values[i], 1e-5); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +TEST (PCL, GASDShapeAndColorEstimationNoInterp) +{ + // Create fake point cloud with colors + pcl::PointCloud::Ptr cloudWithColors (new pcl::PointCloud); + createColorCloud (*cloudWithColors); + + pcl::GASDColorEstimation gasd; + gasd.setInputCloud (cloudWithColors); + + pcl::PointCloud descriptor; + gasd.compute (descriptor); + + const float ref_values[984] = + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.0101, 0, 0, 0, 0, 3.53535, 1.26263, 0, 0, 0, + 1.51515, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.757576, 0, 0, 0, 0, 0.757576, 6.06061, 0, + 0, 0, 0, 4.0404, 1.51515, 0, 0, 0, 0, 6.81818, 0, 0, 0, 0, 0, 1.76768, 1.76768, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0.50505, 0, 0, 0, 0, 0, 3.78788, 0.50505, 0, 0, 0, 0, 5.80808, 0, 0, 0, 0, 0, 2.0202, + 4.29293, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3.78788, 0.252525, 0, 0, 0, 0, 4.79798, 0, 0, 0, 0, + 0, 4.0404, 1.26263, 0, 0, 0, 0, 0.50505, 6.56566, 2.77778, 0, 0, 0, 0, 0.252525, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2.27273, 3.0303, 0, 0, 0, 0, 4.79798, 0, 0, 0, 0, 0, 4.0404, 2.0202, 0, 0, 0, 0, 0.757576, 1.51515, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2.27273, 0, 0, 0, 0, 0.50505, 5.80808, 0, 0, 0, 0, 0, 1.26263, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1.26263, 0.252525, 0.50505, 0.50505, 0.252525, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.757576, 1.0101, 0.757576, 0.50505, 0, 0, 0, 0, 0, + 0.252525, 1.51515, 1.51515, 0.252525, 0, 0.252525, 0.757576, 1.76768, 1.26263, 0, 0, 0.252525, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.50505, 1.26263, 0.757576, 1.51515, 0.757576, + 1.0101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.252525, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.252525, + 0.252525, 0, 0.252525, 0.50505, 0.252525, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1.51515, 1.76768, 1.26263, 1.26263, 0.252525, 0, 0, 0, 0, 0, 0, 0.757576, 0.50505, 0, + 0, 0.757576, 0.252525, 0.252525, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0.252525, 0, 0.252525, 1.51515, 3.28283, 2.0202, 1.76768, 0.252525, 0, 0.50505, 1.26263, 1.0101, 0, 0, 0, + 0.757576, 0.757576, 1.51515, 0.50505, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.50505, 0.50505, 0.757576, 0.50505, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.252525, 0.252525, 0.757576, + 0, 0, 0, 0.252525, 0, 0, 0, 0, 0, 0, 0.252525, 0, 0, 0, 0, 0.252525, 0.50505, 0.252525, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.0101, 1.26263, 1.0101, 0.252525, 2.27273, 1.51515, 0, + 0.50505, 1.26263, 0.757576, 1.0101, 0.252525, 0.252525, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.50505, 0.252525, 0, 0, 0.50505, 0.252525, 0, 0, 1.0101, + 0.757576, 1.76768, 1.76768, 0, 0, 0, 1.76768, 2.77778, 2.52525, 2.27273, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.0101, 1.26263, + 1.0101, 0.50505, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.50505, 0.50505, 0.252525, 0.252525, 0, 0, 0.252525, 0.50505, 0, 0.757576, 0, + 0.50505, 2.52525, 0, 0, 0, 0, 0, 0, 0, 0, 1.26263, 2.52525, 3.53535, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.252525, 0.757576, 0, 0, 0.252525, 0.252525, 0, 0, 0.252525, 0.252525, 0.252525, + 0.252525, 0.50505, 0, 0, 0, 0, 0, 0, 0, 0, 1.26263, 0.757576, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0}; + + EXPECT_EQ (descriptor.points.size (), 1); + for (size_t i = 0; i < size_t (descriptor.points[0].descriptorSize ()); ++i) + { + EXPECT_NEAR (descriptor.points[0].histogram[i], ref_values[i], 1e-5); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +TEST(PCL, GASDShapeAndColorEstimationQuadrilinearInterp) +{ + // Create fake point cloud with colors + pcl::PointCloud::Ptr cloudWithColors (new pcl::PointCloud); + createColorCloud (*cloudWithColors); + + pcl::GASDColorEstimation gasd; + gasd.setInputCloud (cloudWithColors); + gasd.setColorHistsInterpMethod (pcl::INTERP_QUADRILINEAR); + + pcl::PointCloud descriptor; + gasd.compute (descriptor); + + const float ref_values[984] = + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.0101, 0, 0, 0, 0, 3.53535, 1.26263, 0, 0, 0, + 1.51515, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.757576, 0, 0, 0, 0, 0.757576, 6.06061, 0, + 0, 0, 0, 4.0404, 1.51515, 0, 0, 0, 0, 6.81818, 0, 0, 0, 0, 0, 1.76768, 1.76768, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0.50505, 0, 0, 0, 0, 0, 3.78788, 0.50505, 0, 0, 0, 0, 5.80808, 0, 0, 0, 0, 0, 2.0202, + 4.29293, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3.78788, 0.252525, 0, 0, 0, 0, 4.79798, 0, 0, 0, + 0, 0, 4.0404, 1.26263, 0, 0, 0, 0, 0.50505, 6.56566, 2.77778, 0, 0, 0, 0, 0.252525, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2.27273, 3.0303, 0, 0, 0, 0, 4.79798, 0, 0, 0, 0, 0, 4.0404, 2.0202, 0, 0, 0, 0, 0.757576, + 1.51515, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2.27273, 0, 0, 0, 0, 0.50505, 5.80808, 0, 0, + 0, 0, 0, 1.26263, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0.0895643, 0.143288, 0.0720839, 0.125029, 0.171768, 0.140765, 0.00434639, 0.00200408, 0, 0, 0.00597724, + 0.0525044, 0.193269, 0.429334, 0.207777, 0.24761, 0.268739, 0.237959, 0.0413768, 0.00387127, 0, + 0.000931569, 0.00385942, 0.0404541, 0.00418768, 0, 0, 0, 0, 0.00142713, 0.0162888, 0, 0, 0.000403183, + 0, 0, 0, 0.00116143, 0.00354532, 0.00280258, 0.00148538, 0, 0, 0, 0, 0, 0, 0.0123895, 0.248346, 0.591086, + 0.636957, 0.597111, 0.656083, 0.378095, 0.0377745, 0.0129163, 0, 0, 0.130372, 0.659749, 0.927181, 0.498144, + 0.27824, 0.426372, 0.616247, 1.04577, 0.643063, 0.0249504, 0, 0.0606673, 0.0426988, 0.156632, 0.0643944, 0, + 0, 0, 0, 0.0177954, 0.0985798, 0, 0, 0.0262568, 0, 0, 0, 0.00271963, 0.0233101, 0.0331309, 0.0160384, 0, + 0, 0, 0, 0, 0, 0.00471289, 0.3579, 0.851654, 0.893212, 1.1593, 0.81276, 0.451746, 0.00573487, + 0.00665285, 0.0159345, 0.0197507, 0.0100332, 0.116356, 0.258904, 0.285916, 0.137536, 0.329754, 0.201282, + 0.276937, 0.0504019, 0.00140554, 0.00331221, 0.00406454, 0.00205266, 0.00332975, 0.00195687, 0, 0, 0, 0, + 3.03073e-005, 3.03073e-005, 0, 0, 0, 0, 0, 0, 0, 0.000609614, 0.000500804, 1.35678e-005, 0, 0, 0, 0, 0, 0, + 0, 0.0354586, 0.0530443, 0.0349205, 0.0647915, 0.0347517, 0.0334093, 0.000258137, 0.00104701, 0.00399398, + 0.00607182, 0, 0, 0.0145624, 0.0392641, 0.0252404, 0.0542372, 0.0210596, 0.0219414, 0.000380611, + 0.000221201, 0.000827126, 0.00124953, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0.000831429, 0, 0.0802922, 0.12617, 0.158971, 0.151476, 0.134574, 0.0953864, 0.00562285, 0.00363446, + 0.000513492, 0.000831429, 0.000155639, 0, 0.175686, 0.321095, 0.233626, 0.252716, 0.239946, 0.190063, 0.00987096, + 0.00659357, 0.000123905, 0.000155639, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0012772, 0.00426605, 0.00325023, + 0.00231641, 0, 0, 0, 0, 0, 0.0507473, 0.0557701, 0.296648, 0.941834, 1.30216, 1.22476, 1.00798, 0.328648, + 0.0418844, 0.0566177, 0.0853042, 0.107112, 0.0125218, 0.012783, 0.350977, 0.584441, 0.549991, 0.690193, + 0.674605, 0.354125, 0.0598277, 0.0514012, 0.0274164, 0.0305356, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0.0044642, 0.045621, 0.0482378, 0.0215847, 0, 0, 0, 0, 0, 0.149713, 0.0196222, 0.265814, 1.27681, + 2.13404, 2.0593, 1.40056, 0.405919, 0.00728233, 0.233705, 0.784621, 0.679261, 0.0656672, 0.00416686, + 0.167507, 0.862586, 1.01845, 1.07935, 0.827779, 0.200586, 0.00256227, 0.0732096, 0.320982, 0.275378, 0, 0, + 0.0120096, 0.0255571, 0.0166044, 0.0161996, 0.0177707, 0.00366082, 0, 0, 0, 0, 0, 0, 0, 0, 0.0010984, + 0.00114305, 1.79879e-005, 0, 0, 0, 0, 0, 0.0228719, 0, 0.0425383, 0.218586, 0.341904, 0.287174, 0.163829, + 0.0258371, 0.000214485, 0.0198714, 0.118435, 0.11307, 0.0161313, 0, 0.0829918, 0.553307, 0.643753, + 0.581184, 0.474243, 0.0471955, 0.000316249, 0.00722545, 0.0624794, 0.0518481, 0, 0, 0.0144017, 0.0326961, + 0.0204908, 0.0169458, 0.0223265, 0.00392622, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.132962, + 0.0193449, 0.0551697, 0.105996, 0.407031, 0.35539, 0.0566072, 0.0303696, 0.0243085, 0.144756, 0.15231, + 0.130866, 0.147091, 0.0111985, 0.0176582, 0.0786188, 0.24031, 0.179748, 0.0279387, 0.00883565, 0.0148111, + 0.134383, 0.172131, 0.180972, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0.563728, 0.841891, 0.651939, 0.339866, 1.10015, 1.0062, 0.242513, 0.509944, 0.866722, 0.624862, 0.607641, + 0.594272, 0.328508, 0.189046, 0.152908, 0.161903, 0.442358, 0.383888, 0.109291, 0.132222, 0.157232, + 0.276749, 0.317518, 0.356862, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0.436075, 0.432522, 0.0892119, 0.386335, 0.986563, 0.919872, 0.313085, 0.123551, 0.543859, 0.596885, + 0.920122, 1.01302, 0.249186, 0.112221, 0.100269, 0.778301, 1.36733, 1.47131, 0.97278, 0.0962166, + 0.12436, 0.241092, 0.422573, 0.432224, 0, 0, 0.0292873, 0.069272, 0.0241701, 0.0450751, 0.0621059, + 0.0105498, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0145794, 0.000546371, 0.00160651, 0.232425, + 0.411777, 0.279927, 0.0993127, 0.00288046, 5.21399e-005, 0.00594067, 0.0722626, 0.0420616, 0.0115545, + 0.000641368, 0.101776, 0.773579, 1.03488, 0.850195, 0.654514, 0.0566626, 5.28226e-005, 0.00465405, + 0.0493028, 0.0260373, 0, 0, 0.035701, 0.0890744, 0.0369472, 0.0510598, 0.0810702, 0.0114321, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.155581, 0.0081056, 0.0392221, 0.0102823, 0.0121948, 0.0159764, + 0.00453647, 0.016245, 0.0122912, 0.113791, 0.0920094, 0.14147, 0.363948, 0.0102655, 0.0146145, + 0.00654133, 0.0112985, 0.0106292, 0.00172027, 0.00635388, 0.00840588, 0.165742, 0.242665, 0.403613, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 4.03759e-006, 3.95477e-006, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.718571, + 0.431673, 0.22117, 0.0441141, 0.0132298, 0.0152672, 0.0713271, 0.307082, 0.443776, 0.69396, 0.604915, + 0.694449, 1.17028, 0.209737, 0.0721899, 0.022148, 0.0104337, 0.0100016, 0.0245223, 0.12181, 0.152934, + 0.979424, 1.55304, 1.81121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.000199128, 0.000195043, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0.486315, 0.325842, 0.0116061, 0.0347715, 0.180407, 0.21691, 0.0546041, 0.0337822, + 0.322526, 0.517204, 0.312276, 0.276607, 0.460413, 0.185895, 0.00199094, 0.0450161, 0.180782, 0.233176, + 0.0872505, 0.0137579, 0.177266, 0.554998, 0.503438, 0.351193, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.00515609, 0.00142381, 0, 0.00913963, 0.0473098, 0.0779652, 0.0204231, + 0.000522999, 7.43773e-005, 0.000796905, 0.00721695, 0.00654336, 0.00568307, 0.00167137, 0, 0.0142623, + 0.0640188, 0.0967736, 0.0307571, 0.00109069, 7.53513e-005, 0.000807341, 0.007594, 0.00643415, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0}; + + EXPECT_EQ (descriptor.points.size (), 1); + for (size_t i = 0; i < size_t( descriptor.points[0].descriptorSize ()); ++i) + { + EXPECT_NEAR (descriptor.points[0].histogram[i], ref_values[i], 1e-5); + } +} + +/* ---[ */ +int +main (int argc, + char** argv) +{ + if (argc < 2) + { + std::cerr << "No test file given. Please download `bun0.pcd` and pass its path to the test." << std::endl; + return (-1); + } + + cloud.reset (new pcl::PointCloud); + + if (pcl::io::loadPCDFile < pcl::PointXYZ > (argv[1], *cloud) < 0) + { + std::cerr << "Failed to read test file. Please download `bun0.pcd` and pass its path to the test." << std::endl; + return (-1); + } + + testing::InitGoogleTest (&argc, argv); + return (RUN_ALL_TESTS ()); +} +/* ]--- */ diff --git a/test/features/test_pfh_estimation.cpp b/test/features/test_pfh_estimation.cpp index 0bf6c4cf..a92662a0 100644 --- a/test/features/test_pfh_estimation.cpp +++ b/test/features/test_pfh_estimation.cpp @@ -37,9 +37,13 @@ * */ +#include +#ifdef PCL_ONLY_CORE_POINT_TYPES + #define PCL_NO_PRECOMPILE +#endif + #include #include -#include #include #include #include @@ -47,30 +51,32 @@ #include #include -using namespace pcl; -using namespace pcl::io; -using namespace std; - -typedef search::KdTree::Ptr KdTreePtr; +typedef pcl::PointNormal PointT; +typedef pcl::search::KdTree::Ptr KdTreePtr; +using pcl::PointCloud; -PointCloud cloud; -vector indices; -KdTreePtr tree; +static PointCloud::Ptr cloud (new PointCloud ()); +static std::vector indices; +static KdTreePtr tree; /////////////////////////////////////////////////////////////////////////////////// -template void +template class FeatureEstimation, typename PointT, typename NormalT, typename OutputT> void testIndicesAndSearchSurface (const typename PointCloud::Ptr & points, const typename PointCloud::Ptr & normals, - const boost::shared_ptr > & indices, int ndims) + const boost::shared_ptr > & indices, int ndims) + { + typedef pcl::search::KdTree KdTreeT; + typedef FeatureEstimation FeatureEstimationT; + // // Test setIndices and setSearchSurface // PointCloud full_output, output0, output1, output2; // Compute for all points and then subsample the results - FeatureEstimation est0; - est0.setSearchMethod (typename search::KdTree::Ptr (new search::KdTree)); + FeatureEstimationT est0; + est0.setSearchMethod (typename KdTreeT::Ptr (new KdTreeT)); est0.setKSearch (10); est0.setInputCloud (points); est0.setInputNormals (normals); @@ -80,8 +86,8 @@ testIndicesAndSearchSurface (const typename PointCloud::Ptr & points, // Compute with all points as "search surface" and the specified sub-cloud as "input" typename PointCloud::Ptr subpoints (new PointCloud); copyPointCloud (*points, *indices, *subpoints); - FeatureEstimation est1; - est1.setSearchMethod (typename search::KdTree::Ptr (new search::KdTree)); + FeatureEstimationT est1; + est1.setSearchMethod (typename KdTreeT::Ptr (new KdTreeT)); est1.setKSearch (10); est1.setInputCloud (subpoints); est1.setSearchSurface (points); @@ -89,8 +95,8 @@ testIndicesAndSearchSurface (const typename PointCloud::Ptr & points, est1.compute (output1); // Compute with all points as "input" and the specified indices - FeatureEstimation est2; - est2.setSearchMethod (typename search::KdTree::Ptr (new search::KdTree)); + FeatureEstimationT est2; + est2.setSearchMethod (typename KdTreeT::Ptr (new KdTreeT)); est2.setKSearch (10); est2.setInputCloud (points); est2.setInputNormals (normals); @@ -114,13 +120,13 @@ testIndicesAndSearchSurface (const typename PointCloud::Ptr & points, // PointCloud output3, output4; - boost::shared_ptr > indices2 (new vector (0)); + boost::shared_ptr > indices2 (new std::vector (0)); for (size_t i = 0; i < (indices->size ()/2); ++i) indices2->push_back (static_cast (i)); // Compute with all points as search surface + the specified sub-cloud as "input" but for only a subset of indices - FeatureEstimation est3; - est3.setSearchMethod (typename search::KdTree::Ptr (new search::KdTree)); + FeatureEstimationT est3; + est3.setSearchMethod (typename KdTreeT::Ptr (new KdTreeT)); est3.setKSearch (10); est3.setSearchSurface (points); est3.setInputNormals (normals); @@ -145,26 +151,16 @@ testIndicesAndSearchSurface (const typename PointCloud::Ptr & points, ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TEST (PCL, PFHEstimation) { - float f1, f2, f3, f4; + using pcl::PFHSignature125; - // Estimate normals first - NormalEstimation n; - PointCloud::Ptr normals (new PointCloud ()); - // set parameters - n.setInputCloud (cloud.makeShared ()); - boost::shared_ptr > indicesptr (new vector (indices)); - n.setIndices (indicesptr); - n.setSearchMethod (tree); - n.setKSearch (10); // Use 10 nearest neighbors to estimate the normals - // estimate - n.compute (*normals); + float f1, f2, f3, f4; - PFHEstimation pfh; - pfh.setInputNormals (normals); - EXPECT_EQ (pfh.getInputNormals (), normals); + pcl::PFHEstimation pfh; + pfh.setInputNormals (cloud); + EXPECT_EQ (pfh.getInputNormals (), cloud); // computePairFeatures - pfh.computePairFeatures (cloud, *normals, 0, 12, f1, f2, f3, f4); + pfh.computePairFeatures (*cloud, *cloud, 0, 12, f1, f2, f3, f4); EXPECT_NEAR (f1, -0.072575, 1e-4); EXPECT_NEAR (f2, -0.040221, 1e-4); EXPECT_NEAR (f3, 0.068133, 1e-4); @@ -173,7 +169,7 @@ TEST (PCL, PFHEstimation) // computePointPFHSignature int nr_subdiv = 3; Eigen::VectorXf pfh_histogram (nr_subdiv * nr_subdiv * nr_subdiv); - pfh.computePointPFHSignature (cloud, *normals, indices, nr_subdiv, pfh_histogram); + pfh.computePointPFHSignature (*cloud, *cloud, indices, nr_subdiv, pfh_histogram); EXPECT_NEAR (pfh_histogram[0], 0.932506, 1e-2); EXPECT_NEAR (pfh_histogram[1], 2.32429 , 1e-2); EXPECT_NEAR (pfh_histogram[2], 0.357477, 1e-2); @@ -208,9 +204,10 @@ TEST (PCL, PFHEstimation) // Object PointCloud::Ptr pfhs (new PointCloud ()); + boost::shared_ptr > indicesptr (new std::vector (indices)); // set parameters - pfh.setInputCloud (cloud.makeShared ()); + pfh.setInputCloud (cloud); pfh.setIndices (indicesptr); pfh.setSearchMethod (tree); pfh.setKSearch (static_cast (indices.size ())); @@ -254,39 +251,65 @@ TEST (PCL, PFHEstimation) // Test results when setIndices and/or setSearchSurface are used - boost::shared_ptr > test_indices (new vector (0)); - for (size_t i = 0; i < cloud.size (); i+=3) + boost::shared_ptr > test_indices (new std::vector (0)); + for (size_t i = 0; i < cloud->size (); i+=3) test_indices->push_back (static_cast (i)); - testIndicesAndSearchSurface, PointXYZ, Normal, PFHSignature125> - (cloud.makeShared (), normals, test_indices, 125); + testIndicesAndSearchSurface + (cloud, cloud, test_indices, 125); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -TEST (PCL, FPFHEstimation) + +using pcl::FPFHEstimation; +using pcl::FPFHEstimationOMP; +using pcl::FPFHSignature33; + +// "Placeholder" for the type specialized test fixture +template +struct FPFHTest; + +// Template specialization test for FPFHEstimation +template<> +struct FPFHTest > + : public ::testing::Test { - // Estimate normals first - NormalEstimation n; - PointCloud::Ptr normals (new PointCloud ()); - // set parameters - n.setInputCloud (cloud.makeShared ()); - boost::shared_ptr > indicesptr (new vector (indices)); - n.setIndices (indicesptr); - n.setSearchMethod (tree); - n.setKSearch (10); // Use 10 nearest neighbors to estimate the normals - // estimate - n.compute (*normals); + FPFHEstimation fpfh; +}; - FPFHEstimation fpfh; - fpfh.setInputNormals (normals); - EXPECT_EQ (fpfh.getInputNormals (), normals); +// Template specialization test for FPFHEstimationOMP +template<> +struct FPFHTest > + : public ::testing::Test +{ + // Default Constructor is defined to instantiate 4 threads + FPFHTest > () + : fpfh (4) + {} + + FPFHEstimationOMP fpfh; +}; + +// Types which will be instantiated +typedef ::testing::Types, + FPFHEstimationOMP > FPFHEstimatorTypes; +TYPED_TEST_CASE (FPFHTest, FPFHEstimatorTypes); + +// This is a copy of the old FPFHEstimation test which will now +// be applied to both FPFHEstimation and FPFHEstimationOMP +TYPED_TEST (FPFHTest, Estimation) +{ + // Create reference + TypeParam& fpfh = this->fpfh; + fpfh.setInputNormals (cloud); + EXPECT_EQ (fpfh.getInputNormals (), cloud); // computePointSPFHSignature int nr_subdiv = 11; // use the same number of bins for all three angular features Eigen::MatrixXf hist_f1 (indices.size (), nr_subdiv), hist_f2 (indices.size (), nr_subdiv), hist_f3 (indices.size (), nr_subdiv); hist_f1.setZero (); hist_f2.setZero (); hist_f3.setZero (); for (int i = 0; i < static_cast (indices.size ()); ++i) - fpfh.computePointSPFHSignature (cloud, *normals, i, i, indices, hist_f1, hist_f2, hist_f3); + fpfh.computePointSPFHSignature (*cloud, *cloud, i, i, indices, hist_f1, hist_f2, hist_f3); EXPECT_NEAR (hist_f1 (0, 0), 0.757576, 1e-4); EXPECT_NEAR (hist_f1 (0, 1), 0.757576, 1e-4); @@ -327,7 +350,7 @@ TEST (PCL, FPFHEstimation) // weightPointSPFHSignature Eigen::VectorXf fpfh_histogram (nr_subdiv + nr_subdiv + nr_subdiv); fpfh_histogram.setZero (); - vector dists (indices.size ()); + std::vector dists (indices.size ()); for (size_t i = 0; i < dists.size (); ++i) dists[i] = static_cast (i); fpfh.weightPointSPFHSignature (hist_f1, hist_f2, hist_f3, indices, dists, fpfh_histogram); @@ -349,8 +372,8 @@ TEST (PCL, FPFHEstimation) EXPECT_NEAR (fpfh_histogram[15], 16.8062, 1e-2); EXPECT_NEAR (fpfh_histogram[16], 16.2767, 1e-2); EXPECT_NEAR (fpfh_histogram[17], 12.251 , 1e-2); - //EXPECT_NEAR (fpfh_histogram[18], 10.354, 1e-1); - //EXPECT_NEAR (fpfh_histogram[19], 6.65578, 1e-2); + EXPECT_NEAR (fpfh_histogram[18], 10.354, 1e-2); + EXPECT_NEAR (fpfh_histogram[19], 6.65578, 1e-2); EXPECT_NEAR (fpfh_histogram[20], 6.1437 , 1e-2); EXPECT_NEAR (fpfh_histogram[21], 5.83341, 1e-2); EXPECT_NEAR (fpfh_histogram[22], 1.08809, 1e-2); @@ -367,9 +390,10 @@ TEST (PCL, FPFHEstimation) // Object PointCloud::Ptr fpfhs (new PointCloud ()); + boost::shared_ptr > indicesptr (new std::vector (indices)); // set parameters - fpfh.setInputCloud (cloud.makeShared ()); + fpfh.setInputCloud (cloud); fpfh.setNrSubdivisions (11, 11, 11); fpfh.setIndices (indicesptr); fpfh.setSearchMethod (tree); @@ -397,8 +421,8 @@ TEST (PCL, FPFHEstimation) EXPECT_NEAR (fpfhs->points[0].histogram[15], 17.963 , 1e-2); EXPECT_NEAR (fpfhs->points[0].histogram[16], 18.2801, 1e-2); EXPECT_NEAR (fpfhs->points[0].histogram[17], 14.2766, 1e-2); - //EXPECT_NEAR (fpfhs->points[0].histogram[18], 10.8542, 1e-2); - //EXPECT_NEAR (fpfhs->points[0].histogram[19], 6.07925, 1e-2); + EXPECT_NEAR (fpfhs->points[0].histogram[18], 10.8542, 1e-2); + EXPECT_NEAR (fpfhs->points[0].histogram[19], 6.07925, 1e-2); EXPECT_NEAR (fpfhs->points[0].histogram[20], 5.28565, 1e-2); EXPECT_NEAR (fpfhs->points[0].histogram[21], 4.73887, 1e-2); EXPECT_NEAR (fpfhs->points[0].histogram[22], 0.56984, 1e-2); @@ -415,116 +439,28 @@ TEST (PCL, FPFHEstimation) // Test results when setIndices and/or setSearchSurface are used - boost::shared_ptr > test_indices (new vector (0)); - for (size_t i = 0; i < cloud.size (); i+=3) + boost::shared_ptr > test_indices (new std::vector (0)); + for (size_t i = 0; i < cloud->size (); i+=3) test_indices->push_back (static_cast (i)); - testIndicesAndSearchSurface, PointXYZ, Normal, FPFHSignature33> - (cloud.makeShared (), normals, test_indices, 33); - + testIndicesAndSearchSurface + (cloud, cloud, test_indices, 33); } -////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -TEST (PCL, FPFHEstimationOpenMP) -{ - // Estimate normals first - NormalEstimation n; - PointCloud::Ptr normals (new PointCloud ()); - // set parameters - n.setInputCloud (cloud.makeShared ()); - boost::shared_ptr > indicesptr (new vector (indices)); - n.setIndices (indicesptr); - n.setSearchMethod (tree); - n.setKSearch (10); // Use 10 nearest neighbors to estimate the normals - // estimate - n.compute (*normals); - FPFHEstimationOMP fpfh (4); // instantiate 4 threads - fpfh.setInputNormals (normals); - - // Object - PointCloud::Ptr fpfhs (new PointCloud ()); - - // set parameters - fpfh.setInputCloud (cloud.makeShared ()); - fpfh.setNrSubdivisions (11, 11, 11); - fpfh.setIndices (indicesptr); - fpfh.setSearchMethod (tree); - fpfh.setKSearch (static_cast (indices.size ())); - - // estimate - fpfh.compute (*fpfhs); - EXPECT_EQ (fpfhs->points.size (), indices.size ()); - - EXPECT_NEAR (fpfhs->points[0].histogram[0], 1.58591, 1e-3); - EXPECT_NEAR (fpfhs->points[0].histogram[1], 1.68365, 1e-2); - EXPECT_NEAR (fpfhs->points[0].histogram[2], 6.71 , 1e-3); - EXPECT_NEAR (fpfhs->points[0].histogram[3], 23.073, 1e-3); - EXPECT_NEAR (fpfhs->points[0].histogram[4], 33.3828, 1e-2); - EXPECT_NEAR (fpfhs->points[0].histogram[5], 20.4002, 1e-3); - EXPECT_NEAR (fpfhs->points[0].histogram[6], 7.31067, 1e-3); - EXPECT_NEAR (fpfhs->points[0].histogram[7], 1.02635, 1e-3); - EXPECT_NEAR (fpfhs->points[0].histogram[8], 0.48591, 1e-3); - EXPECT_NEAR (fpfhs->points[0].histogram[9], 1.47069, 1e-2); - EXPECT_NEAR (fpfhs->points[0].histogram[10], 2.87061, 1e-3); - EXPECT_NEAR (fpfhs->points[0].histogram[11], 1.78321, 1e-3); - EXPECT_NEAR (fpfhs->points[0].histogram[12], 4.30795, 1e-3); - EXPECT_NEAR (fpfhs->points[0].histogram[13], 7.05514, 1e-3); - EXPECT_NEAR (fpfhs->points[0].histogram[14], 9.37615, 1e-3); - EXPECT_NEAR (fpfhs->points[0].histogram[15], 17.963 , 1e-3); - //EXPECT_NEAR (fpfhs->points[0].histogram[16], 18.2801, 1e-3); - //EXPECT_NEAR (fpfhs->points[0].histogram[17], 14.2766, 1e-3); - //EXPECT_NEAR (fpfhs->points[0].histogram[18], 10.8542, 1e-3); - //EXPECT_NEAR (fpfhs->points[0].histogram[19], 6.07925, 1e-3); - EXPECT_NEAR (fpfhs->points[0].histogram[20], 5.28991, 1e-3); - EXPECT_NEAR (fpfhs->points[0].histogram[21], 4.73438, 1e-3); - EXPECT_NEAR (fpfhs->points[0].histogram[22], 0.56984, 1e-3); - EXPECT_NEAR (fpfhs->points[0].histogram[23], 3.29826, 1e-3); - EXPECT_NEAR (fpfhs->points[0].histogram[24], 5.28156, 1e-3); - EXPECT_NEAR (fpfhs->points[0].histogram[25], 5.26939, 1e-2); - EXPECT_NEAR (fpfhs->points[0].histogram[26], 3.13191, 1e-3); - EXPECT_NEAR (fpfhs->points[0].histogram[27], 1.74453, 1e-3); - EXPECT_NEAR (fpfhs->points[0].histogram[28], 9.41971, 1e-3); - EXPECT_NEAR (fpfhs->points[0].histogram[29], 21.5894, 1e-2); - EXPECT_NEAR (fpfhs->points[0].histogram[30], 24.6302, 1e-3); - EXPECT_NEAR (fpfhs->points[0].histogram[31], 17.7764, 1e-3); - EXPECT_NEAR (fpfhs->points[0].histogram[32], 7.28878, 1e-3); - - // Test results when setIndices and/or setSearchSurface are used - - boost::shared_ptr > test_indices (new vector (0)); - for (size_t i = 0; i < cloud.size (); i+=3) - test_indices->push_back (static_cast (i)); - - testIndicesAndSearchSurface, PointXYZ, Normal, FPFHSignature33> - (cloud.makeShared (), normals, test_indices, 33); -} ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TEST (PCL, VFHEstimation) { - // Estimate normals first - NormalEstimation n; - PointCloud::Ptr normals (new PointCloud ()); - // set parameters - n.setInputCloud (cloud.makeShared ()); - boost::shared_ptr > indicesptr (new vector (indices)); - n.setIndices (indicesptr); - n.setSearchMethod (tree); - n.setKSearch (10); // Use 10 nearest neighbors to estimate the normals - // estimate - n.compute (*normals); - VFHEstimation vfh; - vfh.setInputNormals (normals); - - // PointCloud cloud_normals; - // concatenateFields (cloud, normals, cloud_normals); - // savePCDFile ("bun0_n.pcd", cloud_normals); + using pcl::VFHSignature308; // Object + pcl::VFHEstimation vfh; PointCloud::Ptr vfhs (new PointCloud ()); + boost::shared_ptr > indicesptr (new std::vector (indices)); // set parameters - vfh.setInputCloud (cloud.makeShared ()); + vfh.setInputCloud (cloud); + vfh.setInputNormals (cloud); vfh.setIndices (indicesptr); vfh.setSearchMethod (tree); @@ -539,6 +475,9 @@ TEST (PCL, VFHEstimation) ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TEST (PCL, GFPFH) { + using pcl::PointXYZL; + using pcl::GFPFHSignature16; + PointCloud::Ptr cloud (new PointCloud()); const unsigned num_classes = 3; @@ -561,7 +500,7 @@ TEST (PCL, GFPFH) cloud->width = static_cast (cloud->points.size ()); cloud->height = 1; - GFPFHEstimation gfpfh; + pcl::GFPFHEstimation gfpfh; gfpfh.setNumberOfClasses (num_classes); gfpfh.setOctreeLeafSize (2); gfpfh.setInputCloud (cloud); @@ -585,21 +524,22 @@ main (int argc, char** argv) if (argc < 2) { std::cerr << "No test file given. Please download `bun0.pcd` and pass its path to the test." << std::endl; - return (-1); + return (1); } - if (loadPCDFile (argv[1], cloud) < 0) + if (pcl::io::loadPCDFile (argv[1], *cloud) < 0) { std::cerr << "Failed to read test file. Please download `bun0.pcd` and pass its path to the test." << std::endl; - return (-1); + return (1); } - indices.resize (cloud.points.size ()); - for (size_t i = 0; i < indices.size (); ++i) - indices[i] = static_cast (i); - tree.reset (new search::KdTree (false)); - tree->setInputCloud (cloud.makeShared ()); + indices.reserve (cloud->size ()); + for (size_t i = 0; i < cloud->size (); ++i) + indices.push_back (static_cast (i)); + + tree.reset (new pcl::search::KdTree (false)); + tree->setInputCloud (cloud); testing::InitGoogleTest (&argc, argv); return (RUN_ALL_TESTS ()); diff --git a/test/features/test_rift_estimation.cpp b/test/features/test_rift_estimation.cpp index 13025a9c..933a2a13 100644 --- a/test/features/test_rift_estimation.cpp +++ b/test/features/test_rift_estimation.cpp @@ -118,10 +118,26 @@ TEST (PCL, RIFTEstimation) // Compare to independently verified values const RIFTDescriptor &rift = rift_output.points[220]; - const float correct_rift_feature_values[32] = {0.0187f, 0.0349f, 0.0647f, 0.0881f, 0.0042f, 0.0131f, 0.0346f, 0.0030f, - 0.0076f, 0.0218f, 0.0463f, 0.0030f, 0.0087f, 0.0288f, 0.0920f, 0.0472f, - 0.0076f, 0.0420f, 0.0726f, 0.0669f, 0.0090f, 0.0901f, 0.1274f, 0.2185f, - 0.0147f, 0.1222f, 0.3568f, 0.4348f, 0.0149f, 0.0806f, 0.2787f, 0.6864f}; + float correct_rift_feature_values[32]; + + unsigned major, minor, patch; + std::sscanf (FLANN_VERSION_, "%u.%u.%u", &major, &minor, &patch); + if (PCL_VERSION_CALC (major, minor, patch) > PCL_VERSION_CALC (1, 8, 4)) + { + const float data[32] = {0.0052f, 0.0349f, 0.0647f, 0.0881f, 0.0042f, 0.0131f, 0.0346f, 0.0030f, + 0.0076f, 0.0218f, 0.0463f, 0.0030f, 0.0087f, 0.0288f, 0.0920f, 0.0472f, + 0.0211f, 0.0420f, 0.0726f, 0.0669f, 0.0090f, 0.0901f, 0.1274f, 0.2185f, + 0.0147f, 0.1222f, 0.3568f, 0.4348f, 0.0149f, 0.0806f, 0.2787f, 0.6864f}; + std::copy (data, data + 32, correct_rift_feature_values); + } + else + { + const float data[32] = {0.0187f, 0.0349f, 0.0647f, 0.0881f, 0.0042f, 0.0131f, 0.0346f, 0.0030f, + 0.0076f, 0.0218f, 0.0463f, 0.0030f, 0.0087f, 0.0288f, 0.0920f, 0.0472f, + 0.0076f, 0.0420f, 0.0726f, 0.0669f, 0.0090f, 0.0901f, 0.1274f, 0.2185f, + 0.0147f, 0.1222f, 0.3568f, 0.4348f, 0.0149f, 0.0806f, 0.2787f, 0.6864f}; + std::copy (data, data + 32, correct_rift_feature_values); + } for (int i = 0; i < 32; ++i) EXPECT_NEAR (rift.histogram[i], correct_rift_feature_values[i], 1e-4); } diff --git a/test/features/test_shot_lrf_estimation.cpp b/test/features/test_shot_lrf_estimation.cpp index 2226551f..fd25d072 100644 --- a/test/features/test_shot_lrf_estimation.cpp +++ b/test/features/test_shot_lrf_estimation.cpp @@ -84,7 +84,7 @@ TEST (PCL, SHOTLocalReferenceFrameEstimation) EXPECT_TRUE (pcl_isnan (bunny_LRF.at (24).x_axis[0])); // Expected Results - // point 15: tanget disambiguation + // point 15: tangent disambiguation //float point_15_conf = 0; Eigen::Vector3f point_15_x (-0.849213f, 0.528016f, 0.00593846f); Eigen::Vector3f point_15_y (0.274564f, 0.451135f, -0.849171f); diff --git a/test/filters/CMakeLists.txt b/test/filters/CMakeLists.txt index d36e7c30..253f47f1 100644 --- a/test/filters/CMakeLists.txt +++ b/test/filters/CMakeLists.txt @@ -46,6 +46,9 @@ if (build) FILES test_filters.cpp LINK_WITH pcl_gtest pcl_filters pcl_io pcl_sample_consensus pcl_kdtree pcl_features pcl_segmentation ARGUMENTS "${PCL_SOURCE_DIR}/test/bun0.pcd" "${PCL_SOURCE_DIR}/test/milk_cartoon_all_small_clorox.pcd") + PCL_ADD_TEST(filters_clipper test_clipper + FILES test_clipper.cpp + LINK_WITH pcl_gtest pcl_filters) endif (BUILD_segmentation) endif (BUILD_features) endif (BUILD_io) diff --git a/test/filters/test_clipper.cpp b/test/filters/test_clipper.cpp new file mode 100644 index 00000000..be21f268 --- /dev/null +++ b/test/filters/test_clipper.cpp @@ -0,0 +1,437 @@ +/* + * Software License Agreement (BSD License) + * + * Point Cloud Library (PCL) - www.pointclouds.org + * Copyright (c) 2009-2012, Willow Garage, Inc. + * Copyright (c) 2012-, Open Perception, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the copyright holder(s) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $Id$ + * + */ + +#include +#include +#include +#include +#include + +#include +#include + +using namespace pcl; +using namespace std; +using namespace Eigen; + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +TEST (BoxClipper3D, Filters) +{ + // PointCloud + // ------------------------------------------------------------------------- + + // Create cloud with center point and corner points + PointCloud::Ptr input (new PointCloud ()); + input->push_back (PointXYZ (0.0f, 0.0f, 0.0f)); + input->push_back (PointXYZ (0.9f, 0.9f, 0.9f)); + input->push_back (PointXYZ (0.9f, 0.9f, -0.9f)); + input->push_back (PointXYZ (0.9f, -0.9f, 0.9f)); + input->push_back (PointXYZ (-0.9f, 0.9f, 0.9f)); + input->push_back (PointXYZ (0.9f, -0.9f, -0.9f)); + input->push_back (PointXYZ (-0.9f, -0.9f, 0.9f)); + input->push_back (PointXYZ (-0.9f, 0.9f, -0.9f)); + input->push_back (PointXYZ (-0.9f, -0.9f, -0.9f)); + + ExtractIndices extract_indices; + vector indices; + + BoxClipper3D boxClipper3D (Affine3f::Identity ()); + boxClipper3D.clipPointCloud3D (*input, indices); + + PointCloud cloud_out; + + extract_indices.setInputCloud (input); + extract_indices.setIndices (boost::make_shared > (indices)); + extract_indices.filter (cloud_out); + + EXPECT_EQ (int (indices.size ()), 9); + EXPECT_EQ (int (cloud_out.size ()), 9); + EXPECT_EQ (int (cloud_out.width), 9); + EXPECT_EQ (int (cloud_out.height), 1); + + // Translate points by 1 in Y-axis ... + Affine3f t (Translation3f (0.0f, 1.0f, 0.0f)); + boxClipper3D.setTransformation (t); + boxClipper3D.clipPointCloud3D (*input, indices); + + EXPECT_EQ (int (indices.size ()), 5); + + // ... then rotate points +45 in Y-Axis + t.rotate (AngleAxisf (45.0f * float (M_PI) / 180.0f, Vector3f::UnitY ())); + boxClipper3D.setTransformation (t); + boxClipper3D.clipPointCloud3D (*input, indices); + EXPECT_EQ (int (indices.size ()), 1); + + // ... then rotate points -45 in Z-axis + t.rotate (AngleAxisf (-45.0f * float (M_PI) / 180.0f, Vector3f::UnitZ ())); + boxClipper3D.setTransformation (t); + boxClipper3D.clipPointCloud3D (*input, indices); + EXPECT_EQ (int (indices.size ()), 3); + + // ... then scale points by 2 + t.scale (2.0f); + boxClipper3D.setTransformation (t); + boxClipper3D.clipPointCloud3D (*input, indices); + EXPECT_EQ (int (indices.size ()), 1); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +TEST (CropBox, Filters) +{ + + // PointT + // ------------------------------------------------------------------------- + + // Create cloud with center point and corner points + PointCloud::Ptr input (new PointCloud ()); + + input->push_back (PointXYZ (0.0f, 0.0f, 0.0f)); + input->push_back (PointXYZ (0.9f, 0.9f, 0.9f)); + input->push_back (PointXYZ (0.9f, 0.9f, -0.9f)); + input->push_back (PointXYZ (0.9f, -0.9f, 0.9f)); + input->push_back (PointXYZ (-0.9f, 0.9f, 0.9f)); + input->push_back (PointXYZ (0.9f, -0.9f, -0.9f)); + input->push_back (PointXYZ (-0.9f, -0.9f, 0.9f)); + input->push_back (PointXYZ (-0.9f, 0.9f, -0.9f)); + input->push_back (PointXYZ (-0.9f, -0.9f, -0.9f)); + + // Test the PointCloud method + CropBox cropBoxFilter (true); + cropBoxFilter.setInputCloud (input); + Eigen::Vector4f min_pt (-1.0f, -1.0f, -1.0f, 1.0f); + Eigen::Vector4f max_pt (1.0f, 1.0f, 1.0f, 1.0f); + + // Cropbox slightly bigger then bounding box of points + cropBoxFilter.setMin (min_pt); + cropBoxFilter.setMax (max_pt); + + // Indices + vector indices; + cropBoxFilter.filter (indices); + + // Cloud + PointCloud cloud_out; + cropBoxFilter.filter (cloud_out); + + // Should contain all + EXPECT_EQ (int (indices.size ()), 9); + EXPECT_EQ (int (cloud_out.size ()), 9); + EXPECT_EQ (int (cloud_out.width), 9); + EXPECT_EQ (int (cloud_out.height), 1); + + IndicesConstPtr removed_indices; + removed_indices = cropBoxFilter.getRemovedIndices (); + EXPECT_EQ (int (removed_indices->size ()), 0); + + // Test setNegative + PointCloud cloud_out_negative; + cropBoxFilter.setNegative (true); + cropBoxFilter.filter (cloud_out_negative); + EXPECT_EQ (int (cloud_out_negative.size ()), 0); + + cropBoxFilter.filter (indices); + EXPECT_EQ (int (indices.size ()), 0); + + cropBoxFilter.setNegative (false); + cropBoxFilter.filter (cloud_out); + + // Translate crop box up by 1 + cropBoxFilter.setTranslation(Eigen::Vector3f(0, 1, 0)); + cropBoxFilter.filter (indices); + cropBoxFilter.filter (cloud_out); + + EXPECT_EQ (int (indices.size ()), 5); + EXPECT_EQ (int (cloud_out.size ()), 5); + + removed_indices = cropBoxFilter.getRemovedIndices (); + EXPECT_EQ (int (removed_indices->size ()), 4); + + // Test setNegative + cropBoxFilter.setNegative (true); + cropBoxFilter.filter (cloud_out_negative); + EXPECT_EQ (int (cloud_out_negative.size ()), 4); + + cropBoxFilter.filter (indices); + EXPECT_EQ (int (indices.size ()), 4); + + cropBoxFilter.setNegative (false); + cropBoxFilter.filter (cloud_out); + + // Rotate crop box up by 45 + cropBoxFilter.setRotation (Eigen::Vector3f (0.0f, 45.0f * float (M_PI) / 180.0f, 0.0f)); + cropBoxFilter.filter (indices); + cropBoxFilter.filter (cloud_out); + + EXPECT_EQ (int (indices.size ()), 1); + EXPECT_EQ (int (cloud_out.size ()), 1); + EXPECT_EQ (int (cloud_out.width), 1); + EXPECT_EQ (int (cloud_out.height), 1); + + removed_indices = cropBoxFilter.getRemovedIndices (); + EXPECT_EQ (int (removed_indices->size ()), 8); + + // Test setNegative + cropBoxFilter.setNegative (true); + cropBoxFilter.filter (cloud_out_negative); + EXPECT_EQ (int (cloud_out_negative.size ()), 8); + + cropBoxFilter.filter (indices); + EXPECT_EQ (int (indices.size ()), 8); + + cropBoxFilter.setNegative (false); + cropBoxFilter.filter (cloud_out); + + // Rotate point cloud by -45 + cropBoxFilter.setTransform (getTransformation (0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -45.0f * float (M_PI) / 180.0f)); + cropBoxFilter.filter (indices); + cropBoxFilter.filter (cloud_out); + + EXPECT_EQ (int (indices.size ()), 3); + EXPECT_EQ (int (cloud_out.size ()), 3); + EXPECT_EQ (int (cloud_out.width), 3); + EXPECT_EQ (int (cloud_out.height), 1); + + removed_indices = cropBoxFilter.getRemovedIndices (); + EXPECT_EQ (int (removed_indices->size ()), 6); + + // Test setNegative + cropBoxFilter.setNegative (true); + cropBoxFilter.filter (cloud_out_negative); + EXPECT_EQ (int (cloud_out_negative.size ()), 6); + + cropBoxFilter.filter (indices); + EXPECT_EQ (int (indices.size ()), 6); + + cropBoxFilter.setNegative (false); + cropBoxFilter.filter (cloud_out); + + // Translate point cloud down by -1 + cropBoxFilter.setTransform (getTransformation(0, -1, 0, 0, 0, -45.0 * float (M_PI) / 180.0)); + cropBoxFilter.filter (indices); + cropBoxFilter.filter (cloud_out); + + EXPECT_EQ (int (indices.size ()), 2); + EXPECT_EQ (int (cloud_out.size ()), 2); + EXPECT_EQ (int (cloud_out.width), 2); + EXPECT_EQ (int (cloud_out.height), 1); + + removed_indices = cropBoxFilter.getRemovedIndices (); + EXPECT_EQ (int (removed_indices->size ()), 7); + + // Test setNegative + cropBoxFilter.setNegative (true); + cropBoxFilter.filter (cloud_out_negative); + EXPECT_EQ (int (cloud_out_negative.size ()), 7); + + cropBoxFilter.filter (indices); + EXPECT_EQ (int (indices.size ()), 7); + + cropBoxFilter.setNegative (false); + cropBoxFilter.filter (cloud_out); + + // Remove point cloud rotation + cropBoxFilter.setTransform (getTransformation(0, -1, 0, 0, 0, 0)); + cropBoxFilter.filter (indices); + cropBoxFilter.filter (cloud_out); + + EXPECT_EQ (int (indices.size ()), 0); + EXPECT_EQ (int (cloud_out.size ()), 0); + EXPECT_EQ (int (cloud_out.width), 0); + EXPECT_EQ (int (cloud_out.height), 1); + + removed_indices = cropBoxFilter.getRemovedIndices (); + EXPECT_EQ (int (removed_indices->size ()), 9); + + // Test setNegative + cropBoxFilter.setNegative (true); + cropBoxFilter.filter (cloud_out_negative); + EXPECT_EQ (int (cloud_out_negative.size ()), 9); + + cropBoxFilter.filter (indices); + EXPECT_EQ (int (indices.size ()), 9); + + // PCLPointCloud2 + // ------------------------------------------------------------------------- + + // Create cloud with center point and corner points + PCLPointCloud2::Ptr input2 (new PCLPointCloud2); + pcl::toPCLPointCloud2 (*input, *input2); + + // Test the PointCloud method + CropBox cropBoxFilter2(true); + cropBoxFilter2.setInputCloud (input2); + + // Cropbox slightly bigger then bounding box of points + cropBoxFilter2.setMin (min_pt); + cropBoxFilter2.setMax (max_pt); + + // Indices + vector indices2; + cropBoxFilter2.filter (indices2); + + // Cloud + PCLPointCloud2 cloud_out2; + cropBoxFilter2.filter (cloud_out2); + + // Should contain all + EXPECT_EQ (int (indices2.size ()), 9); + EXPECT_EQ (int (indices2.size ()), int (cloud_out2.width * cloud_out2.height)); + + IndicesConstPtr removed_indices2; + removed_indices2 = cropBoxFilter2.getRemovedIndices (); + EXPECT_EQ (int (removed_indices2->size ()), 0); + + // Test setNegative + PCLPointCloud2 cloud_out2_negative; + cropBoxFilter2.setNegative (true); + cropBoxFilter2.filter (cloud_out2_negative); + EXPECT_EQ (int (cloud_out2_negative.width), 0); + + cropBoxFilter2.filter (indices2); + EXPECT_EQ (int (indices2.size ()), 0); + + cropBoxFilter2.setNegative (false); + cropBoxFilter2.filter (cloud_out2); + + // Translate crop box up by 1 + cropBoxFilter2.setTranslation (Eigen::Vector3f(0, 1, 0)); + cropBoxFilter2.filter (indices2); + cropBoxFilter2.filter (cloud_out2); + + EXPECT_EQ (int (indices2.size ()), 5); + EXPECT_EQ (int (indices2.size ()), int (cloud_out2.width * cloud_out2.height)); + + removed_indices2 = cropBoxFilter2.getRemovedIndices (); + EXPECT_EQ (int (removed_indices2->size ()), 4); + + // Test setNegative + cropBoxFilter2.setNegative (true); + cropBoxFilter2.filter (cloud_out2_negative); + EXPECT_EQ (int (cloud_out2_negative.width), 4); + + cropBoxFilter2.filter (indices2); + EXPECT_EQ (int (indices2.size ()), 4); + + cropBoxFilter2.setNegative (false); + cropBoxFilter2.filter (cloud_out2); + + // Rotate crop box up by 45 + cropBoxFilter2.setRotation (Eigen::Vector3f (0.0f, 45.0f * float (M_PI) / 180.0f, 0.0f)); + cropBoxFilter2.filter (indices2); + cropBoxFilter2.filter (cloud_out2); + + EXPECT_EQ (int (indices2.size ()), 1); + EXPECT_EQ (int (indices2.size ()), int (cloud_out2.width * cloud_out2.height)); + + // Rotate point cloud by -45 + cropBoxFilter2.setTransform (getTransformation (0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -45.0f * float (M_PI) / 180.0f)); + cropBoxFilter2.filter (indices2); + cropBoxFilter2.filter (cloud_out2); + + EXPECT_EQ (int (indices2.size ()), 3); + EXPECT_EQ (int (cloud_out2.width * cloud_out2.height), 3); + + removed_indices2 = cropBoxFilter2.getRemovedIndices (); + EXPECT_EQ (int (removed_indices2->size ()), 6); + + // Test setNegative + cropBoxFilter2.setNegative (true); + cropBoxFilter2.filter (cloud_out2_negative); + EXPECT_EQ (int (cloud_out2_negative.width), 6); + + cropBoxFilter2.filter (indices2); + EXPECT_EQ (int (indices2.size ()), 6); + + cropBoxFilter2.setNegative (false); + cropBoxFilter2.filter (cloud_out2); + + // Translate point cloud down by -1 + cropBoxFilter2.setTransform (getTransformation (0.0f, -1.0f, 0.0f, 0.0f, 0.0f, -45.0f * float (M_PI) / 180.0f)); + cropBoxFilter2.filter (indices2); + cropBoxFilter2.filter (cloud_out2); + + EXPECT_EQ (int (indices2.size ()), 2); + EXPECT_EQ (int (cloud_out2.width * cloud_out2.height), 2); + + removed_indices2 = cropBoxFilter2.getRemovedIndices (); + EXPECT_EQ (int (removed_indices2->size ()), 7); + + // Test setNegative + cropBoxFilter2.setNegative (true); + cropBoxFilter2.filter (cloud_out2_negative); + EXPECT_EQ (int (cloud_out2_negative.width), 7); + + cropBoxFilter2.filter (indices2); + EXPECT_EQ (int (indices2.size ()), 7); + + cropBoxFilter2.setNegative (false); + cropBoxFilter2.filter (cloud_out2); + + // Remove point cloud rotation + cropBoxFilter2.setTransform (getTransformation(0, -1, 0, 0, 0, 0)); + cropBoxFilter2.filter (indices2); + cropBoxFilter2.filter (cloud_out2); + + EXPECT_EQ (int (indices2.size ()), 0); + EXPECT_EQ (int (cloud_out2.width * cloud_out2.height), 0); + + removed_indices2 = cropBoxFilter2.getRemovedIndices (); + EXPECT_EQ (int (removed_indices2->size ()), 9); + + // Test setNegative + cropBoxFilter2.setNegative (true); + cropBoxFilter2.filter (cloud_out2_negative); + EXPECT_EQ (int (cloud_out2_negative.width), 9); + + cropBoxFilter2.filter (indices2); + EXPECT_EQ (int (indices2.size ()), 9); + + cropBoxFilter2.setNegative (false); + cropBoxFilter2.filter (cloud_out2); +} + +/* ---[ */ +int +main (int argc, char** argv) +{ + testing::InitGoogleTest (&argc, argv); + return (RUN_ALL_TESTS ()); +} +/* ]--- */ diff --git a/test/filters/test_filters.cpp b/test/filters/test_filters.cpp index e414b917..81771dad 100644 --- a/test/filters/test_filters.cpp +++ b/test/filters/test_filters.cpp @@ -54,7 +54,6 @@ #include #include #include -#include #include #include @@ -896,11 +895,9 @@ TEST (VoxelGrid_RGB, Filters) for (int i = 0; i < 10; ++i) { PointXYZRGB pt; - int rgb = (col_r[i] << 16) | (col_g[i] << 8) | col_b[i]; - pt.x = 0.0f; - pt.y = 0.0f; - pt.z = 0.0f; - pt.rgb = *reinterpret_cast (&rgb); + pt.r = col_r[i]; + pt.g = col_g[i]; + pt.b = col_b[i]; cloud_rgb_.points.push_back (pt); } @@ -1072,7 +1069,7 @@ TEST (VoxelGrid_XYZNormal, Filters) PointCloud output; input->reserve (16); input->is_dense = false; - + PointNormal point; PointNormal ground_truth[2][2][2]; for (unsigned zIdx = 0; zIdx < 2; ++zIdx) @@ -1087,25 +1084,25 @@ TEST (VoxelGrid_XYZNormal, Filters) // y = 1, z = 0 -> orthogonal normals // y = 1, z = 1 -> random normals PointNormal& voxel = ground_truth [xIdx][yIdx][zIdx]; - + point.x = xIdx * 1.99; point.y = yIdx * 1.99; point.z = zIdx * 1.99; point.normal_x = getRandomNumber (1.0, -1.0); point.normal_y = getRandomNumber (1.0, -1.0); point.normal_z = getRandomNumber (1.0, -1.0); - + float norm = 1.0f / sqrt (point.normal_x * point.normal_x + point.normal_y * point.normal_y + point.normal_z * point.normal_z ); point.normal_x *= norm; point.normal_y *= norm; point.normal_z *= norm; - + // std::cout << "adding point: " << point.x << " , " << point.y << " , " << point.z // << " -- " << point.normal_x << " , " << point.normal_y << " , " << point.normal_z << std::endl; input->push_back (point); - + voxel = point; - + if (xIdx != 0) { point.x = getRandomNumber (0.99) + float (xIdx); @@ -1140,11 +1137,11 @@ TEST (VoxelGrid_XYZNormal, Filters) voxel.x += point.x; voxel.y += point.y; voxel.z += point.z; - + voxel.x *= 0.5; voxel.y *= 0.5; voxel.z *= 0.5; - + if (yIdx == 0 && zIdx == 0) { voxel.normal_x = std::numeric_limits::quiet_NaN (); @@ -1157,13 +1154,13 @@ TEST (VoxelGrid_XYZNormal, Filters) point.normal_x *= norm; point.normal_y *= norm; point.normal_z *= norm; - + voxel.normal_x += point.normal_x; voxel.normal_y += point.normal_y; voxel.normal_z += point.normal_z; - + norm = 1.0f / sqrt (voxel.normal_x * voxel.normal_x + voxel.normal_y * voxel.normal_y + voxel.normal_z * voxel.normal_z ); - + voxel.normal_x *= norm; voxel.normal_y *= norm; voxel.normal_z *= norm; @@ -1173,17 +1170,17 @@ TEST (VoxelGrid_XYZNormal, Filters) input->push_back (point); // std::cout << "voxel: " << voxel.x << " , " << voxel.y << " , " << voxel.z // << " -- " << voxel.normal_x << " , " << voxel.normal_y << " , " << voxel.normal_z << std::endl; - + } } } - + VoxelGrid grid; grid.setLeafSize (1.0f, 1.0f, 1.0f); grid.setFilterLimits (0.0, 2.0); grid.setInputCloud (input); grid.filter (output); - + // check the output for (unsigned idx = 0, zIdx = 0; zIdx < 2; ++zIdx) { @@ -1197,7 +1194,7 @@ TEST (VoxelGrid_XYZNormal, Filters) EXPECT_EQ (voxel.x, point.x); EXPECT_EQ (voxel.y, point.y); EXPECT_EQ (voxel.z, point.z); - + if (pcl_isfinite(voxel.normal_x) || pcl_isfinite (point.normal_x)) { EXPECT_EQ (voxel.normal_x, point.normal_x); @@ -1207,10 +1204,10 @@ TEST (VoxelGrid_XYZNormal, Filters) } } } - + toPCLPointCloud2 (*input, cloud_blob_); cloud_blob_ptr_.reset (new PCLPointCloud2 (cloud_blob_)); - + VoxelGrid grid2; PCLPointCloud2 output_blob; @@ -1233,7 +1230,7 @@ TEST (VoxelGrid_XYZNormal, Filters) EXPECT_EQ (voxel.x, point.x); EXPECT_EQ (voxel.y, point.y); EXPECT_EQ (voxel.z, point.z); - + if (pcl_isfinite(voxel.normal_x) || pcl_isfinite (point.normal_x)) { EXPECT_EQ (voxel.normal_x, point.normal_x); @@ -1297,7 +1294,7 @@ TEST (VoxelGridCovariance, Filters) EXPECT_LE (fabs (output.points[neighbors.at (0)].y - output.points[centroidIdx].y), 0.02); EXPECT_LE (output.points[neighbors.at (0)].z - output.points[centroidIdx].z, 0.02 * 2); - // testing seach functions + // testing search functions grid.setSaveLeafLayout (false); grid.filter (output, true); @@ -1436,321 +1433,6 @@ TEST (RadiusOutlierRemoval, Filters) EXPECT_NEAR (cloud_out.points[cloud_out.points.size () - 1].z, -0.021299, 1e-4); } -////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -TEST (CropBox, Filters) -{ - - // PointT - // ------------------------------------------------------------------------- - - // Create cloud with center point and corner points - PointCloud::Ptr input (new PointCloud ()); - - input->push_back (PointXYZ (0.0f, 0.0f, 0.0f)); - input->push_back (PointXYZ (0.9f, 0.9f, 0.9f)); - input->push_back (PointXYZ (0.9f, 0.9f, -0.9f)); - input->push_back (PointXYZ (0.9f, -0.9f, 0.9f)); - input->push_back (PointXYZ (-0.9f, 0.9f, 0.9f)); - input->push_back (PointXYZ (0.9f, -0.9f, -0.9f)); - input->push_back (PointXYZ (-0.9f, -0.9f, 0.9f)); - input->push_back (PointXYZ (-0.9f, 0.9f, -0.9f)); - input->push_back (PointXYZ (-0.9f, -0.9f, -0.9f)); - - // Test the PointCloud method - CropBox cropBoxFilter (true); - cropBoxFilter.setInputCloud (input); - Eigen::Vector4f min_pt (-1.0f, -1.0f, -1.0f, 1.0f); - Eigen::Vector4f max_pt (1.0f, 1.0f, 1.0f, 1.0f); - - // Cropbox slighlty bigger then bounding box of points - cropBoxFilter.setMin (min_pt); - cropBoxFilter.setMax (max_pt); - - // Indices - vector indices; - cropBoxFilter.filter (indices); - - // Cloud - PointCloud cloud_out; - cropBoxFilter.filter (cloud_out); - - // Should contain all - EXPECT_EQ (int (indices.size ()), 9); - EXPECT_EQ (int (cloud_out.size ()), 9); - EXPECT_EQ (int (cloud_out.width), 9); - EXPECT_EQ (int (cloud_out.height), 1); - - IndicesConstPtr removed_indices; - removed_indices = cropBoxFilter.getRemovedIndices (); - EXPECT_EQ (int (removed_indices->size ()), 0); - - // Test setNegative - PointCloud cloud_out_negative; - cropBoxFilter.setNegative (true); - cropBoxFilter.filter (cloud_out_negative); - EXPECT_EQ (int (cloud_out_negative.size ()), 0); - - cropBoxFilter.filter (indices); - EXPECT_EQ (int (indices.size ()), 0); - - cropBoxFilter.setNegative (false); - cropBoxFilter.filter (cloud_out); - - // Translate crop box up by 1 - cropBoxFilter.setTranslation(Eigen::Vector3f(0, 1, 0)); - cropBoxFilter.filter (indices); - cropBoxFilter.filter (cloud_out); - - EXPECT_EQ (int (indices.size ()), 5); - EXPECT_EQ (int (cloud_out.size ()), 5); - - removed_indices = cropBoxFilter.getRemovedIndices (); - EXPECT_EQ (int (removed_indices->size ()), 4); - - // Test setNegative - cropBoxFilter.setNegative (true); - cropBoxFilter.filter (cloud_out_negative); - EXPECT_EQ (int (cloud_out_negative.size ()), 4); - - cropBoxFilter.filter (indices); - EXPECT_EQ (int (indices.size ()), 4); - - cropBoxFilter.setNegative (false); - cropBoxFilter.filter (cloud_out); - - // Rotate crop box up by 45 - cropBoxFilter.setRotation (Eigen::Vector3f (0.0f, 45.0f * float (M_PI) / 180.0f, 0.0f)); - cropBoxFilter.filter (indices); - cropBoxFilter.filter (cloud_out); - - EXPECT_EQ (int (indices.size ()), 1); - EXPECT_EQ (int (cloud_out.size ()), 1); - EXPECT_EQ (int (cloud_out.width), 1); - EXPECT_EQ (int (cloud_out.height), 1); - - removed_indices = cropBoxFilter.getRemovedIndices (); - EXPECT_EQ (int (removed_indices->size ()), 8); - - // Test setNegative - cropBoxFilter.setNegative (true); - cropBoxFilter.filter (cloud_out_negative); - EXPECT_EQ (int (cloud_out_negative.size ()), 8); - - cropBoxFilter.filter (indices); - EXPECT_EQ (int (indices.size ()), 8); - - cropBoxFilter.setNegative (false); - cropBoxFilter.filter (cloud_out); - - // Rotate point cloud by -45 - cropBoxFilter.setTransform (getTransformation (0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -45.0f * float (M_PI) / 180.0f)); - cropBoxFilter.filter (indices); - cropBoxFilter.filter (cloud_out); - - EXPECT_EQ (int (indices.size ()), 3); - EXPECT_EQ (int (cloud_out.size ()), 3); - EXPECT_EQ (int (cloud_out.width), 3); - EXPECT_EQ (int (cloud_out.height), 1); - - removed_indices = cropBoxFilter.getRemovedIndices (); - EXPECT_EQ (int (removed_indices->size ()), 6); - - // Test setNegative - cropBoxFilter.setNegative (true); - cropBoxFilter.filter (cloud_out_negative); - EXPECT_EQ (int (cloud_out_negative.size ()), 6); - - cropBoxFilter.filter (indices); - EXPECT_EQ (int (indices.size ()), 6); - - cropBoxFilter.setNegative (false); - cropBoxFilter.filter (cloud_out); - - // Translate point cloud down by -1 - cropBoxFilter.setTransform (getTransformation(0, -1, 0, 0, 0, -45.0 * float (M_PI) / 180.0)); - cropBoxFilter.filter (indices); - cropBoxFilter.filter (cloud_out); - - EXPECT_EQ (int (indices.size ()), 2); - EXPECT_EQ (int (cloud_out.size ()), 2); - EXPECT_EQ (int (cloud_out.width), 2); - EXPECT_EQ (int (cloud_out.height), 1); - - removed_indices = cropBoxFilter.getRemovedIndices (); - EXPECT_EQ (int (removed_indices->size ()), 7); - - // Test setNegative - cropBoxFilter.setNegative (true); - cropBoxFilter.filter (cloud_out_negative); - EXPECT_EQ (int (cloud_out_negative.size ()), 7); - - cropBoxFilter.filter (indices); - EXPECT_EQ (int (indices.size ()), 7); - - cropBoxFilter.setNegative (false); - cropBoxFilter.filter (cloud_out); - - // Remove point cloud rotation - cropBoxFilter.setTransform (getTransformation(0, -1, 0, 0, 0, 0)); - cropBoxFilter.filter (indices); - cropBoxFilter.filter (cloud_out); - - EXPECT_EQ (int (indices.size ()), 0); - EXPECT_EQ (int (cloud_out.size ()), 0); - EXPECT_EQ (int (cloud_out.width), 0); - EXPECT_EQ (int (cloud_out.height), 1); - - removed_indices = cropBoxFilter.getRemovedIndices (); - EXPECT_EQ (int (removed_indices->size ()), 9); - - // Test setNegative - cropBoxFilter.setNegative (true); - cropBoxFilter.filter (cloud_out_negative); - EXPECT_EQ (int (cloud_out_negative.size ()), 9); - - cropBoxFilter.filter (indices); - EXPECT_EQ (int (indices.size ()), 9); - - // PCLPointCloud2 - // ------------------------------------------------------------------------- - - // Create cloud with center point and corner points - PCLPointCloud2::Ptr input2 (new PCLPointCloud2); - pcl::toPCLPointCloud2 (*input, *input2); - - // Test the PointCloud method - CropBox cropBoxFilter2(true); - cropBoxFilter2.setInputCloud (input2); - - // Cropbox slighlty bigger then bounding box of points - cropBoxFilter2.setMin (min_pt); - cropBoxFilter2.setMax (max_pt); - - // Indices - vector indices2; - cropBoxFilter2.filter (indices2); - - // Cloud - PCLPointCloud2 cloud_out2; - cropBoxFilter2.filter (cloud_out2); - - // Should contain all - EXPECT_EQ (int (indices2.size ()), 9); - EXPECT_EQ (int (indices2.size ()), int (cloud_out2.width * cloud_out2.height)); - - IndicesConstPtr removed_indices2; - removed_indices2 = cropBoxFilter2.getRemovedIndices (); - EXPECT_EQ (int (removed_indices2->size ()), 0); - - // Test setNegative - PCLPointCloud2 cloud_out2_negative; - cropBoxFilter2.setNegative (true); - cropBoxFilter2.filter (cloud_out2_negative); - EXPECT_EQ (int (cloud_out2_negative.width), 0); - - cropBoxFilter2.filter (indices2); - EXPECT_EQ (int (indices2.size ()), 0); - - cropBoxFilter2.setNegative (false); - cropBoxFilter2.filter (cloud_out2); - - // Translate crop box up by 1 - cropBoxFilter2.setTranslation (Eigen::Vector3f(0, 1, 0)); - cropBoxFilter2.filter (indices2); - cropBoxFilter2.filter (cloud_out2); - - EXPECT_EQ (int (indices2.size ()), 5); - EXPECT_EQ (int (indices2.size ()), int (cloud_out2.width * cloud_out2.height)); - - removed_indices2 = cropBoxFilter2.getRemovedIndices (); - EXPECT_EQ (int (removed_indices2->size ()), 4); - - // Test setNegative - cropBoxFilter2.setNegative (true); - cropBoxFilter2.filter (cloud_out2_negative); - EXPECT_EQ (int (cloud_out2_negative.width), 4); - - cropBoxFilter2.filter (indices2); - EXPECT_EQ (int (indices2.size ()), 4); - - cropBoxFilter2.setNegative (false); - cropBoxFilter2.filter (cloud_out2); - - // Rotate crop box up by 45 - cropBoxFilter2.setRotation (Eigen::Vector3f (0.0f, 45.0f * float (M_PI) / 180.0f, 0.0f)); - cropBoxFilter2.filter (indices2); - cropBoxFilter2.filter (cloud_out2); - - EXPECT_EQ (int (indices2.size ()), 1); - EXPECT_EQ (int (indices2.size ()), int (cloud_out2.width * cloud_out2.height)); - - // Rotate point cloud by -45 - cropBoxFilter2.setTransform (getTransformation (0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -45.0f * float (M_PI) / 180.0f)); - cropBoxFilter2.filter (indices2); - cropBoxFilter2.filter (cloud_out2); - - EXPECT_EQ (int (indices2.size ()), 3); - EXPECT_EQ (int (cloud_out2.width * cloud_out2.height), 3); - - removed_indices2 = cropBoxFilter2.getRemovedIndices (); - EXPECT_EQ (int (removed_indices2->size ()), 6); - - // Test setNegative - cropBoxFilter2.setNegative (true); - cropBoxFilter2.filter (cloud_out2_negative); - EXPECT_EQ (int (cloud_out2_negative.width), 6); - - cropBoxFilter2.filter (indices2); - EXPECT_EQ (int (indices2.size ()), 6); - - cropBoxFilter2.setNegative (false); - cropBoxFilter2.filter (cloud_out2); - - // Translate point cloud down by -1 - cropBoxFilter2.setTransform (getTransformation (0.0f, -1.0f, 0.0f, 0.0f, 0.0f, -45.0f * float (M_PI) / 180.0f)); - cropBoxFilter2.filter (indices2); - cropBoxFilter2.filter (cloud_out2); - - EXPECT_EQ (int (indices2.size ()), 2); - EXPECT_EQ (int (cloud_out2.width * cloud_out2.height), 2); - - removed_indices2 = cropBoxFilter2.getRemovedIndices (); - EXPECT_EQ (int (removed_indices2->size ()), 7); - - // Test setNegative - cropBoxFilter2.setNegative (true); - cropBoxFilter2.filter (cloud_out2_negative); - EXPECT_EQ (int (cloud_out2_negative.width), 7); - - cropBoxFilter2.filter (indices2); - EXPECT_EQ (int (indices2.size ()), 7); - - cropBoxFilter2.setNegative (false); - cropBoxFilter2.filter (cloud_out2); - - // Remove point cloud rotation - cropBoxFilter2.setTransform (getTransformation(0, -1, 0, 0, 0, 0)); - cropBoxFilter2.filter (indices2); - cropBoxFilter2.filter (cloud_out2); - - EXPECT_EQ (int (indices2.size ()), 0); - EXPECT_EQ (int (cloud_out2.width * cloud_out2.height), 0); - - removed_indices2 = cropBoxFilter2.getRemovedIndices (); - EXPECT_EQ (int (removed_indices2->size ()), 9); - - // Test setNegative - cropBoxFilter2.setNegative (true); - cropBoxFilter2.filter (cloud_out2_negative); - EXPECT_EQ (int (cloud_out2_negative.width), 9); - - cropBoxFilter2.filter (indices2); - EXPECT_EQ (int (indices2.size ()), 9); - - cropBoxFilter2.setNegative (false); - cropBoxFilter2.filter (cloud_out2); -} - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TEST (StatisticalOutlierRemoval, Filters) { @@ -2184,11 +1866,11 @@ TEST (FrustumCulling, Filters) //Creating a point cloud on the XY plane PointCloud::Ptr input (new PointCloud ()); - for (int i = 0; i < 5; i++) + for (int i = 0; i < 5; i++) { - for (int j = 0; j < 5; j++) + for (int j = 0; j < 5; j++) { - for (int k = 0; k < 5; k++) + for (int k = 0; k < 5; k++) { pcl::PointXYZ pt; pt.x = float (i); @@ -2227,7 +1909,7 @@ TEST (FrustumCulling, Filters) pcl::PointCloud ::Ptr output (new pcl::PointCloud ); fc.filter (*output); - + // Should filter all points in the input cloud EXPECT_EQ (output->points.size (), input->points.size ()); pcl::IndicesConstPtr removed; @@ -2245,13 +1927,13 @@ TEST (FrustumCulling, Filters) EXPECT_EQ (output->size (), input->size ()); for (size_t i = 0; i < output->size (); i++) { - EXPECT_TRUE (pcl_isnan (output->at (i).x)); + EXPECT_TRUE (pcl_isnan (output->at (i).x)); EXPECT_TRUE (pcl_isnan (output->at (i).y)); EXPECT_TRUE (pcl_isnan (output->at (i).z)); } removed = fc.getRemovedIndices (); EXPECT_EQ (removed->size (), input->size ()); - + } @@ -2325,7 +2007,7 @@ TEST (ConditionalRemovalTfQuadraticXYZComparison, Filters) cyl_comp->setComparisonMatrix (planeMatrix); cyl_comp->setComparisonVector (planeVector); cyl_comp->setComparisonScalar (-2 * 5.0); - cyl_comp->setComparisonOperator (ComparisonOps::LT); + cyl_comp->setComparisonOperator (ComparisonOps::LT); condrem.filter (output); @@ -2468,39 +2150,39 @@ TEST (NormalRefinement, Filters) /* * Initialization of parameters */ - + // Input without NaN pcl::PointCloud cloud_organized_nonan; std::vector dummy; pcl::removeNaNFromPointCloud (*cloud_organized, cloud_organized_nonan, dummy); - + // Viewpoint const float vp_x = cloud_organized_nonan.sensor_origin_[0]; const float vp_y = cloud_organized_nonan.sensor_origin_[1]; const float vp_z = cloud_organized_nonan.sensor_origin_[2]; - + // Search parameters const int k = 5; std::vector > k_indices; std::vector > k_sqr_distances; - + // Estimated and refined normal containers pcl::PointCloud cloud_organized_normal; pcl::PointCloud cloud_organized_normal_refined; - + /* * Neighbor search */ - + // Search for neighbors pcl::search::KdTree kdtree; kdtree.setInputCloud (cloud_organized_nonan.makeShared ()); kdtree.nearestKSearch (cloud_organized_nonan, std::vector (), k, k_indices, k_sqr_distances); - + /* * Estimate normals */ - + // Run estimation pcl::NormalEstimation ne; cloud_organized_normal.reserve (cloud_organized_nonan.size ()); @@ -2517,22 +2199,22 @@ TEST (NormalRefinement, Filters) // Store cloud_organized_normal.push_back (normali); } - + /* * Refine normals */ - + // Run refinement pcl::NormalRefinement nr (k_indices, k_sqr_distances); nr.setInputCloud (cloud_organized_normal.makeShared()); nr.setMaxIterations (15); nr.setConvergenceThreshold (0.00001f); nr.filter (cloud_organized_normal_refined); - + /* * Find dominant plane in the scene */ - + // Calculate SAC model pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients); pcl::PointIndices::Ptr inliers (new pcl::PointIndices); @@ -2543,42 +2225,42 @@ TEST (NormalRefinement, Filters) seg.setDistanceThreshold (0.005); seg.setInputCloud (cloud_organized_normal.makeShared ()); seg.segment (*inliers, *coefficients); - + // Read out SAC model const std::vector& idx_table = inliers->indices; float a = coefficients->values[0]; float b = coefficients->values[1]; float c = coefficients->values[2]; const float d = coefficients->values[3]; - + // Find a point on the plane [0 0 z] => z = -d / c pcl::PointXYZ p_table; p_table.x = 0.0f; p_table.y = 0.0f; p_table.z = -d / c; - + // Use the point to orient the SAC normal correctly - pcl::flipNormalTowardsViewpoint (p_table, vp_x, vp_y, vp_z, a, b, c); - + pcl::flipNormalTowardsViewpoint (p_table, vp_x, vp_y, vp_z, a, b, c); + /* * Test: check that the refined table normals are closer to the SAC model normal */ - + // Errors for the two normal sets and their means std::vector errs_est; float err_est_mean = 0.0f; std::vector errs_refined; float err_refined_mean = 0.0f; - + // Number of zero or NaN vectors produced by refinement int num_zeros = 0; int num_nans = 0; - + // Loop for (unsigned int i = 0; i < idx_table.size (); ++i) { float tmp; - + // Estimated (need to avoid zeros and NaNs) const pcl::PointXYZRGBNormal& calci = cloud_organized_normal[idx_table[i]]; if ((fabsf (calci.normal_x) + fabsf (calci.normal_y) + fabsf (calci.normal_z)) > 0.0f) @@ -2590,7 +2272,7 @@ TEST (NormalRefinement, Filters) err_est_mean += tmp; } } - + // Refined const pcl::PointXYZRGBNormal& refinedi = cloud_organized_normal_refined[idx_table[i]]; if ((fabsf (refinedi.normal_x) + fabsf (refinedi.normal_y) + fabsf (refinedi.normal_z)) > 0.0f) @@ -2612,27 +2294,27 @@ TEST (NormalRefinement, Filters) ++num_zeros; } } - + // Mean errors err_est_mean /= static_cast (errs_est.size ()); err_refined_mean /= static_cast (errs_refined.size ()); - + // Error variance of estimated float err_est_var = 0.0f; for (unsigned int i = 0; i < errs_est.size (); ++i) err_est_var = (errs_est[i] - err_est_mean) * (errs_est[i] - err_est_mean); err_est_var /= static_cast (errs_est.size () - 1); - + // Error variance of refined float err_refined_var = 0.0f; for (unsigned int i = 0; i < errs_refined.size (); ++i) err_refined_var = (errs_refined[i] - err_refined_mean) * (errs_refined[i] - err_refined_mean); err_refined_var /= static_cast (errs_refined.size () - 1); - + // Refinement should not produce any zeros and NaNs EXPECT_EQ(num_zeros, 0); EXPECT_EQ(num_nans, 0); - + // Expect mean/variance of error of refined to be smaller, i.e. closer to SAC model EXPECT_LT(err_refined_mean, err_est_mean); EXPECT_LT(err_refined_var, err_est_var); diff --git a/test/filters/test_sampling.cpp b/test/filters/test_sampling.cpp index 99bd31d0..30fce28b 100644 --- a/test/filters/test_sampling.cpp +++ b/test/filters/test_sampling.cpp @@ -66,39 +66,40 @@ TEST (CovarianceSampling, Filters) covariance_sampling.setNormals (cloud_walls_normals); covariance_sampling.setNumberOfSamples (static_cast (cloud_walls_normals->size ()) / 4); double cond_num_walls = covariance_sampling.computeConditionNumber (); - EXPECT_NEAR (113.29773, cond_num_walls, 1e-1); + + // Conditioning number should be loosely close to the expected number. Adopting 10% of the reference value + EXPECT_NEAR (113.29773, cond_num_walls, 10.); IndicesPtr walls_indices (new std::vector ()); covariance_sampling.filter (*walls_indices); - covariance_sampling.setIndices (walls_indices); double cond_num_walls_sampled = covariance_sampling.computeConditionNumber (); - EXPECT_NEAR (22.11506, cond_num_walls_sampled, 1e-1); - EXPECT_EQ (686, (*walls_indices)[0]); - EXPECT_EQ (1900, (*walls_indices)[walls_indices->size () / 4]); - EXPECT_EQ (1278, (*walls_indices)[walls_indices->size () / 2]); - EXPECT_EQ (2960, (*walls_indices)[walls_indices->size () * 3 / 4]); - EXPECT_EQ (2060, (*walls_indices)[walls_indices->size () - 1]); + // Conditioning number should be loosely close to the expected number. Adopting 10% of the reference value + EXPECT_NEAR (22.11506, cond_num_walls_sampled, 2.); + + // Ensure it respects the requested sampling size + EXPECT_EQ (static_cast (cloud_walls_normals->size ()) / 4, walls_indices->size ()); covariance_sampling.setInputCloud (cloud_turtle_normals); covariance_sampling.setNormals (cloud_turtle_normals); covariance_sampling.setIndices (IndicesPtr ()); covariance_sampling.setNumberOfSamples (static_cast (cloud_turtle_normals->size ()) / 8); double cond_num_turtle = covariance_sampling.computeConditionNumber (); - EXPECT_NEAR (cond_num_turtle, 20661.7663, 0.5); + + // Conditioning number should be loosely close to the expected number. Adopting 10% of the reference value + EXPECT_NEAR (cond_num_turtle, 20661.7663, 2e4); IndicesPtr turtle_indices (new std::vector ()); covariance_sampling.filter (*turtle_indices); covariance_sampling.setIndices (turtle_indices); double cond_num_turtle_sampled = covariance_sampling.computeConditionNumber (); - EXPECT_NEAR (cond_num_turtle_sampled, 5795.5057, 0.5); - EXPECT_EQ ((*turtle_indices)[0], 80344); - EXPECT_EQ ((*turtle_indices)[turtle_indices->size () / 4], 145982); - EXPECT_EQ ((*turtle_indices)[turtle_indices->size () / 2], 104557); - EXPECT_EQ ((*turtle_indices)[turtle_indices->size () * 3 / 4], 41512); - EXPECT_EQ ((*turtle_indices)[turtle_indices->size () - 1], 136885); + // Conditioning number should be loosely close to the expected number. Adopting 10% of the reference value + EXPECT_NEAR (cond_num_turtle_sampled, 5795.5057, 5e3); + + // Ensure it respects the requested sampling size + EXPECT_EQ (static_cast (cloud_turtle_normals->size ()) / 8, turtle_indices->size ()); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/test/io/CMakeLists.txt b/test/io/CMakeLists.txt index 1872e6b3..4483a330 100644 --- a/test/io/CMakeLists.txt +++ b/test/io/CMakeLists.txt @@ -25,6 +25,11 @@ if (build) FILES test_grabbers.cpp LINK_WITH pcl_gtest pcl_io ARGUMENTS "${PCL_SOURCE_DIR}/test/grabber_sequences") + + PCL_ADD_TEST(io_ply_io test_ply_io + FILES test_ply_io.cpp + LINK_WITH pcl_gtest pcl_io) + # Uses VTK readers to verify if (VTK_FOUND AND NOT ANDROID) include_directories(${VTK_INCLUDE_DIRS}) diff --git a/test/io/test_grabbers.cpp b/test/io/test_grabbers.cpp index 25c5b75e..0a3ac400 100644 --- a/test/io/test_grabbers.cpp +++ b/test/io/test_grabbers.cpp @@ -49,7 +49,7 @@ TEST (PCL, PCDGrabber) grabber.registerCallback (fxn); grabber.start (); // 1 second should be /plenty/ of time - boost::this_thread::sleep (boost::posix_time::microseconds (1E6)); + boost::this_thread::sleep (boost::posix_time::seconds (1)); grabber.stop (); //// Make sure they match @@ -129,7 +129,9 @@ TEST (PCL, ImageGrabberTIFF) boost::this_thread::sleep (boost::posix_time::microseconds (10000)); if (++niter > 100) { - ASSERT_TRUE (false); + #ifdef PCL_BUILT_WITH_VTK + FAIL (); + #endif return; } } @@ -398,7 +400,9 @@ TEST (PCL, ImageGrabberSetIntrinsicsTIFF) boost::this_thread::sleep (boost::posix_time::microseconds (10000)); if (++niter > 100) { - ASSERT_TRUE (false); + #ifdef PCL_BUILT_WITH_VTK + FAIL (); + #endif return; } } diff --git a/test/io/test_io.cpp b/test/io/test_io.cpp index 6b4af4cb..2922cdb9 100644 --- a/test/io/test_io.cpp +++ b/test/io/test_io.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -90,7 +91,7 @@ TEST (PCL, ComplexPCDFileASCII) EXPECT_EQ (blob.fields[1].name, "_"); EXPECT_EQ (blob.fields[1].offset, 4 * 33); EXPECT_EQ (blob.fields[1].count, 10); - EXPECT_EQ (blob.fields[1].datatype, pcl::PCLPointField::FLOAT32); + EXPECT_EQ (blob.fields[1].datatype, (uint8_t) -1); EXPECT_EQ (blob.fields[2].name, "x"); EXPECT_EQ (blob.fields[2].offset, 4 * 33 + 10 * 1); @@ -156,6 +157,8 @@ TEST (PCL, ComplexPCDFileASCII) EXPECT_EQ (val[30], 0); EXPECT_EQ (val[31], 0); EXPECT_EQ (val[32], 0); + + remove ("complex_ascii.pcd"); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -255,6 +258,8 @@ TEST (PCL, AllTypesPCDFile) EXPECT_FLOAT_EQ (float (b8), -250.05f); memcpy (&b8, &blob.data[blob.fields[7].offset + sizeof (double)], sizeof (double)); EXPECT_FLOAT_EQ (float (b8), -251.05f); + + remove ("all_types.pcd"); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -652,6 +657,10 @@ TEST (PCL, IO) EXPECT_FLOAT_EQ (cloud.points[0].y, first.y); // test for fromPCLPointCloud2 () EXPECT_FLOAT_EQ (cloud.points[0].z, first.z); // test for fromPCLPointCloud2 () EXPECT_FLOAT_EQ (cloud.points[0].intensity, first.intensity); // test for fromPCLPointCloud2 () + + remove ("test_pcl_io_ascii.pcd"); + remove ("test_pcl_io_binary.pcd"); + remove ("test_pcl_io.pcd"); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -720,6 +729,8 @@ TEST (PCL, PCDReaderWriter) EXPECT_FLOAT_EQ (cloud.points[nr_p - 1].y, last.y); // test for fromPCLPointCloud2 () EXPECT_FLOAT_EQ (cloud.points[nr_p - 1].z, last.z); // test for fromPCLPointCloud2 () EXPECT_FLOAT_EQ (cloud.points[nr_p - 1].intensity, last.intensity); // test for fromPCLPointCloud2 () + + remove ("test_pcl_io.pcd"); } TEST (PCL, PCDReaderWriterASCIIColorPrecision) @@ -763,10 +774,13 @@ TEST (PCL, PCDReaderWriterASCIIColorPrecision) EXPECT_EQ (cloud[i].g, cloud_ascii[i].g); EXPECT_EQ (cloud[i].b, cloud_ascii[i].b); } + + remove ("temp_binary_color.pcd"); + remove ("temp_ascii_color.pcd"); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -TEST (PCL, ASCIIReader) +TEST (PCL, ASCIIRead) { PointCloud cloud, rcloud; @@ -806,240 +820,98 @@ TEST (PCL, ASCIIReader) EXPECT_FLOAT_EQ(cloud.points[i].intensity, rcloud.points[i].intensity); } + remove ("test_pcd.txt"); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -TEST (PCL, PLYReaderWriter) +TEST(PCL, OBJRead) { - pcl::PCLPointCloud2 cloud_blob, cloud_blob2; - PointCloud cloud, cloud2; - - cloud.width = 640; - cloud.height = 480; - cloud.resize (cloud.width * cloud.height); - cloud.is_dense = true; - - srand (static_cast (time (NULL))); - size_t nr_p = cloud.size (); - // Randomly create a new point cloud - for (size_t i = 0; i < nr_p; ++i) - { - cloud[i].x = static_cast (1024 * rand () / (RAND_MAX + 1.0)); - cloud[i].y = static_cast (1024 * rand () / (RAND_MAX + 1.0)); - cloud[i].z = static_cast (1024 * rand () / (RAND_MAX + 1.0)); - cloud[i].intensity = static_cast (i); - } - - // Convert from data type to blob - toPCLPointCloud2 (cloud, cloud_blob); - - EXPECT_EQ (cloud_blob.width, cloud.width); // test for toPCLPointCloud2 () - EXPECT_EQ (cloud_blob.height, cloud.height); // test for toPCLPointCloud2 () - EXPECT_EQ (cloud_blob.is_dense, cloud.is_dense); // test for toPCLPointCloud2 () - EXPECT_EQ (cloud_blob.data.size (), - cloud_blob.width * cloud_blob.height * sizeof (PointXYZI)); + std::ofstream fs; + fs.open ("test_obj.obj"); + fs << "# Blender v2.79 (sub 4) OBJ File: ''\n" + "mtllib test_obj.mtl\n" + "o Cube_Cube.001\n" + "v -1.000000 -1.000000 1.000000\n" + "v -1.000000 1.000000 1.000000\n" + "v -1.000000 -1.000000 -1.000000\n" + "v -1.000000 1.000000 -1.000000\n" + "v 1.000000 -1.000000 1.000000\n" + "v 1.000000 1.000000 1.000000\n" + "v 1.000000 -1.000000 -1.000000\n" + "v 1.000000 1.000000 -1.000000\n" + "vn -1.0000 0.0000 0.0000\n" + "vn 0.0000 0.0000 -1.0000\n" + "vn 1.0000 0.0000 0.0000\n" + "vn 0.0000 0.0000 1.0000\n" + "vn 0.0000 -1.0000 0.0000\n" + "vn 0.0000 1.0000 0.0000\n" + "# Redundant vertex normal to test error handling\n" + "vn 0.0000 0.0000 0.0000\n" + "usemtl None\n" + "s off\n" + "f 1//1 2//1 4//1 3//1\n" + "f 3//2 4//2 8//2 7//2\n" + "f 7//3 8//3 6//3 5//3\n" + "f 5//4 6//4 2//4 1//4\n" + "f 3//5 7//5 5//5 1//5\n" + "f 8//6 4//6 2//6 6//6\n"; - // test for toPCLPointCloud2 () - PLYWriter writer; - writer.write ("test_pcl_io.ply", cloud_blob, Eigen::Vector4f::Zero (), Eigen::Quaternionf::Identity (), true, true); + fs.close (); + fs.open ("test_obj.mtl"); + fs << "# Blender MTL File: 'None'\n" + "# Material Count: 1\n" + "newmtl None\n" + "Ns 0\n" + "Ka 0.000000 0.000000 0.000000\n" + "Kd 0.8 0.8 0.8\n" + "Ks 0.8 0.8 0.8\n" + "d 1\n" + "illum 2\n"; - PLYReader reader; - reader.read ("test_pcl_io.ply", cloud_blob2); - //PLY DOES preserve organiziation - EXPECT_EQ (cloud_blob.width * cloud_blob.height, cloud_blob2.width * cloud_blob2.height); - EXPECT_EQ (cloud_blob.is_dense, cloud.is_dense); - EXPECT_EQ (size_t (cloud_blob2.data.size ()), // PointXYZI is 16*2 (XYZ+1, Intensity+3) - cloud_blob2.width * cloud_blob2.height * sizeof (PointXYZ)); // test for loadPLYFile () + fs.close (); - // Convert from blob to data type - fromPCLPointCloud2 (cloud_blob2, cloud2); + pcl::PCLPointCloud2 blob; + pcl::OBJReader objreader = pcl::OBJReader(); + int res = objreader.read ("test_obj.obj", blob); + EXPECT_NE (int (res), -1); + EXPECT_EQ (blob.width, 8); + EXPECT_EQ (blob.height, 1); + EXPECT_EQ (blob.is_dense, true); + EXPECT_EQ (blob.data.size (), 8 * 6 * 4); // 8 verts, 6 values per vertex (vx,vy,vz,vnx,vny,vnz), 4 byte per value - // EXPECT_EQ (cloud.width, cloud2.width); // test for fromPCLPointCloud2 () - // EXPECT_EQ (cloud.height, cloud2.height); // test for fromPCLPointCloud2 () - // EXPECT_EQ (cloud.is_dense, cloud2.is_dense); // test for fromPCLPointCloud2 () - EXPECT_EQ (cloud.size (), cloud2.size ()); // test for fromPCLPointCloud2 () + // Check fields + EXPECT_EQ (blob.fields[0].name, "x"); + EXPECT_EQ (blob.fields[0].offset, 0); + EXPECT_EQ (blob.fields[0].count, 1); + EXPECT_EQ (blob.fields[0].datatype, pcl::PCLPointField::FLOAT32); - for (uint32_t counter = 0; counter < cloud.size (); ++counter) - { - EXPECT_FLOAT_EQ (cloud[counter].x, cloud2[counter].x); // test for fromPCLPointCloud2 () - EXPECT_FLOAT_EQ (cloud[counter].y, cloud2[counter].y); // test for fromPCLPointCloud2 () - EXPECT_FLOAT_EQ (cloud[counter].z, cloud2[counter].z); // test for fromPCLPointCloud2 () - EXPECT_FLOAT_EQ (cloud[counter].intensity, cloud2[counter].intensity); // test for fromPCLPointCloud2 () - } -} + EXPECT_EQ (blob.fields[1].name, "y"); + EXPECT_EQ (blob.fields[1].offset, 4 * 1); + EXPECT_EQ (blob.fields[1].count, 1); + EXPECT_EQ (blob.fields[1].datatype, pcl::PCLPointField::FLOAT32); -////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -class PLYTest : public ::testing::Test -{ - protected: + EXPECT_EQ (blob.fields[2].name, "z"); + EXPECT_EQ (blob.fields[2].offset, 4 * 2); + EXPECT_EQ (blob.fields[2].count, 1); + EXPECT_EQ (blob.fields[2].datatype, pcl::PCLPointField::FLOAT32); - PLYTest () : mesh_file_ply_("ply_color_mesh.ply") - { - std::ofstream fs; - fs.open (mesh_file_ply_.c_str ()); - fs << "ply\n" - "format ascii 1.0\n" - "element vertex 4\n" - "property float x\n" - "property float y\n" - "property float z\n" - "property uchar red\n" - "property uchar green\n" - "property uchar blue\n" - "property uchar alpha\n" - "element face 2\n" - "property list uchar int vertex_indices\n" - "end_header\n" - "4.23607 0 1.61803 255 0 0 255\n" - "2.61803 2.61803 2.61803 0 255 0 0\n" - "0 1.61803 4.23607 0 0 255 128\n" - "0 -1.61803 4.23607 255 255 255 128\n" - "3 0 1 2\n" - "3 1 2 3\n"; - fs.close (); - - // Test colors from ply_benchmark.ply - rgba_1_ = static_cast (255) << 24 | static_cast (255) << 16 | - static_cast (0) << 8 | static_cast (0); - rgba_2_ = static_cast (0) << 24 | static_cast (0) << 16 | - static_cast (255) << 8 | static_cast (0); - rgba_3_ = static_cast (128) << 24 | static_cast (0) << 16 | - static_cast (0) << 8 | static_cast (255); - rgba_4_ = static_cast (128) << 24 | static_cast (255) << 16 | - static_cast (255) << 8 | static_cast (255); - } + EXPECT_EQ (blob.fields[3].name, "normal_x"); + EXPECT_EQ (blob.fields[3].offset, 4 * 3); + EXPECT_EQ (blob.fields[3].count, 1); + EXPECT_EQ (blob.fields[3].datatype, pcl::PCLPointField::FLOAT32); - virtual - ~PLYTest () { remove (mesh_file_ply_.c_str ()); } + EXPECT_EQ (blob.fields[4].name, "normal_y"); + EXPECT_EQ (blob.fields[4].offset, 4 * 4); + EXPECT_EQ (blob.fields[4].count, 1); + EXPECT_EQ (blob.fields[4].datatype, pcl::PCLPointField::FLOAT32); - std::string mesh_file_ply_; - uint32_t rgba_1_; - uint32_t rgba_2_; - uint32_t rgba_3_; - uint32_t rgba_4_; -}; + EXPECT_EQ (blob.fields[5].name, "normal_z"); + EXPECT_EQ (blob.fields[5].offset, 4 * 5); + EXPECT_EQ (blob.fields[5].count, 1); + EXPECT_EQ (blob.fields[5].datatype, pcl::PCLPointField::FLOAT32); -////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -TEST_F (PLYTest, LoadPLYFileColoredASCIIIntoBlob) -{ - int res; - uint32_t rgba; - - PCLPointCloud2 cloud_blob; - uint32_t ps; - int32_t offset = -1; - - // check if loading is ok - res = loadPLYFile (mesh_file_ply_, cloud_blob); - ASSERT_EQ (res, 0); - - // blob has proper structure - EXPECT_EQ (cloud_blob.height, 1); - EXPECT_EQ (cloud_blob.width, 4); - EXPECT_EQ (cloud_blob.fields.size(), 4); - EXPECT_FALSE (cloud_blob.is_bigendian); - EXPECT_EQ (cloud_blob.point_step, 16); - EXPECT_EQ (cloud_blob.row_step, 16 * 4); - EXPECT_EQ (cloud_blob.data.size(), 16 * 4); - // EXPECT_TRUE (cloud_blob.is_dense); // this is failing and it shouldnt? - - // scope blob data - ps = cloud_blob.point_step; - for (size_t i = 0; i < cloud_blob.fields.size (); ++i) - if (cloud_blob.fields[i].name == std::string("rgba")) - offset = static_cast (cloud_blob.fields[i].offset); - - ASSERT_GE (offset, 0); - - // 1st point - rgba = *reinterpret_cast (&cloud_blob.data[offset]); - ASSERT_EQ (rgba, rgba_1_); - - // 2th point - rgba = *reinterpret_cast (&cloud_blob.data[ps + offset]); - ASSERT_EQ (rgba, rgba_2_); - - // 3th point - rgba = *reinterpret_cast (&cloud_blob.data[2 * ps + offset]); - ASSERT_EQ (rgba, rgba_3_); - - // 4th point - rgba = *reinterpret_cast (&cloud_blob.data[3 * ps + offset]); - ASSERT_EQ (rgba, rgba_4_); -} - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -TEST_F (PLYTest, LoadPLYFileColoredASCIIIntoPolygonMesh) -{ - int res; - uint32_t rgba; - PolygonMesh mesh; - uint32_t ps; - int32_t offset = -1; - - // check if loading is ok - res = loadPLYFile (mesh_file_ply_, mesh); - ASSERT_EQ (res, 0); - - // blob has proper structure - EXPECT_EQ (mesh.cloud.height, 1); - EXPECT_EQ (mesh.cloud.width, 4); - EXPECT_EQ (mesh.cloud.fields.size(), 4); - EXPECT_FALSE (mesh.cloud.is_bigendian); - EXPECT_EQ (mesh.cloud.point_step, 16); - EXPECT_EQ (mesh.cloud.row_step, 16 * 4); - EXPECT_EQ (mesh.cloud.data.size(), 16 * 4); - // EXPECT_TRUE (mesh.cloud.is_dense); // this is failing and it shouldnt? - - // scope blob data - ps = mesh.cloud.point_step; - for (size_t i = 0; i < mesh.cloud.fields.size (); ++i) - if (mesh.cloud.fields[i].name == std::string("rgba")) - offset = static_cast (mesh.cloud.fields[i].offset); - - ASSERT_GE (offset, 0); - - // 1st point - rgba = *reinterpret_cast (&mesh.cloud.data[offset]); - ASSERT_EQ (rgba, rgba_1_); - - // 2th point - rgba = *reinterpret_cast (&mesh.cloud.data[ps + offset]); - ASSERT_EQ (rgba, rgba_2_); - - // 3th point - rgba = *reinterpret_cast (&mesh.cloud.data[2 * ps + offset]); - ASSERT_EQ (rgba, rgba_3_); - - // 4th point - rgba = *reinterpret_cast (&mesh.cloud.data[3 * ps + offset]); - ASSERT_EQ (rgba, rgba_4_); -} - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -template class PLYPointCloudTest : public PLYTest { }; -typedef ::testing::Types RGBPointTypes; -TYPED_TEST_CASE (PLYPointCloudTest, RGBPointTypes); -TYPED_TEST (PLYPointCloudTest, LoadPLYFileColoredASCIIIntoPointCloud) -{ - int res; - PointCloud cloud_rgb; - - // check if loading is ok - res = loadPLYFile (PLYTest::mesh_file_ply_, cloud_rgb); - ASSERT_EQ (res, 0); - - // cloud has proper structure - EXPECT_EQ (cloud_rgb.height, 1); - EXPECT_EQ (cloud_rgb.width, 4); - EXPECT_EQ (cloud_rgb.points.size(), 4); - // EXPECT_TRUE (cloud_rgb.is_dense); // this is failing and it shouldnt? - - // scope cloud data - ASSERT_EQ (cloud_rgb[0].rgba, PLYTest::rgba_1_); - ASSERT_EQ (cloud_rgb[1].rgba, PLYTest::rgba_2_); - ASSERT_EQ (cloud_rgb[2].rgba, PLYTest::rgba_3_); - ASSERT_EQ (cloud_rgb[3].rgba, PLYTest::rgba_4_); + remove ("test_obj.obj"); + remove ("test_obj.mtl"); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1092,6 +964,8 @@ TEST (PCL, ExtendedIO) ASSERT_EQ (cloud.points[0].histogram[i], i); ASSERT_EQ (cloud.points[1].histogram[i], 33-i); } + + remove ("v.pcd"); } @@ -1207,7 +1081,8 @@ TEST (PCL, LZF) EXPECT_EQ (res, 0); PCDReader reader; - reader.read ("test_pcl_io_compressed.pcd", cloud2); + res = reader.read ("test_pcl_io_compressed.pcd", cloud2); + EXPECT_EQ (res, 0); EXPECT_EQ (cloud2.width, cloud.width); EXPECT_EQ (cloud2.height, cloud.height); @@ -1272,8 +1147,79 @@ TEST (PCL, LZFExtended) EXPECT_EQ (res, 0); PCDReader reader; - reader.read ("test_pcl_io_compressed.pcd", cloud2); + res = reader.read ("test_pcl_io_compressed.pcd", cloud2); + EXPECT_EQ (res, 0); + + EXPECT_EQ (cloud2.width, blob.width); + EXPECT_EQ (cloud2.height, blob.height); + EXPECT_EQ (cloud2.is_dense, cloud.is_dense); + EXPECT_EQ (cloud2.points.size (), cloud.points.size ()); + + for (size_t i = 0; i < cloud2.points.size (); ++i) + { + EXPECT_EQ (cloud2.points[i].x, cloud.points[i].x); + EXPECT_EQ (cloud2.points[i].y, cloud.points[i].y); + EXPECT_EQ (cloud2.points[i].z, cloud.points[i].z); + EXPECT_EQ (cloud2.points[i].normal_x, cloud.points[i].normal_x); + EXPECT_EQ (cloud2.points[i].normal_y, cloud.points[i].normal_y); + EXPECT_EQ (cloud2.points[i].normal_z, cloud.points[i].normal_z); + EXPECT_EQ (cloud2.points[i].rgb, cloud.points[i].rgb); + } + + remove ("test_pcl_io_compressed.pcd"); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +TEST (PCL, LZFInMem) +{ + PointCloud cloud; + cloud.width = 640; + cloud.height = 480; + cloud.points.resize (cloud.width * cloud.height); + cloud.is_dense = true; + + srand (static_cast (time (NULL))); + size_t nr_p = cloud.points.size (); + // Randomly create a new point cloud + for (size_t i = 0; i < nr_p; ++i) + { + cloud.points[i].x = static_cast (1024 * rand () / (RAND_MAX + 1.0)); + cloud.points[i].y = static_cast (1024 * rand () / (RAND_MAX + 1.0)); + cloud.points[i].z = static_cast (1024 * rand () / (RAND_MAX + 1.0)); + cloud.points[i].normal_x = static_cast (1024 * rand () / (RAND_MAX + 1.0)); + cloud.points[i].normal_y = static_cast (1024 * rand () / (RAND_MAX + 1.0)); + cloud.points[i].normal_z = static_cast (1024 * rand () / (RAND_MAX + 1.0)); + cloud.points[i].rgb = static_cast (1024 * rand () / (RAND_MAX + 1.0)); + } + + pcl::PCLPointCloud2 blob; + pcl::toPCLPointCloud2 (cloud, blob); + std::ostringstream oss; + PCDWriter writer; + int res = writer.writeBinaryCompressed (oss, blob); + EXPECT_EQ (res, 0); + std::string pcd_str = oss.str (); + + Eigen::Vector4f origin; + Eigen::Quaternionf orientation; + int pcd_version = -1; + int data_type = -1; + unsigned int data_idx = 0; + std::istringstream iss (pcd_str, std::ios::binary); + PCDReader reader; + pcl::PCLPointCloud2 blob2; + res = reader.readHeader (iss, blob2, origin, orientation, pcd_version, data_type, data_idx); + EXPECT_EQ (res, 0); + EXPECT_EQ (blob2.width, blob.width); + EXPECT_EQ (blob2.height, blob.height); + EXPECT_EQ (data_type, 2); // since it was written by writeBinaryCompressed(), it should be compressed. + + const unsigned char *data = reinterpret_cast (pcd_str.data ()); + res = reader.readBodyBinary (data, blob2, pcd_version, data_type == 2, data_idx); + PointCloud cloud2; + pcl::fromPCLPointCloud2 (blob2, cloud2); + EXPECT_EQ (res, 0); EXPECT_EQ (cloud2.width, blob.width); EXPECT_EQ (cloud2.height, blob.height); EXPECT_EQ (cloud2.is_dense, cloud.is_dense); @@ -1366,6 +1312,8 @@ TEST (PCL, Locale) catch (const std::exception&) { } + + remove ("test_pcl_io_ascii.pcd"); #endif } diff --git a/test/io/test_octree_compression.cpp b/test/io/test_octree_compression.cpp index d2521d69..8b97fcab 100644 --- a/test/io/test_octree_compression.cpp +++ b/test/io/test_octree_compression.cpp @@ -167,7 +167,7 @@ TEST(PCL, OctreeDeCompressionFile) EXPECT_EQ(rv, 0) << " loadPCDFile " << pcd_file; EXPECT_GT((int) input_cloud_ptr->width , 0) << "invalid point cloud width from " << pcd_file; - EXPECT_GT((int) input_cloud_ptr->height, 0) << "invalid point cloud heigth from " << pcd_file; + EXPECT_GT((int) input_cloud_ptr->height, 0) << "invalid point cloud height from " << pcd_file; // iterate over compression profiles for (int compression_profile = pcl::io::LOW_RES_ONLINE_COMPRESSION_WITHOUT_COLOR; diff --git a/test/io/test_ply_io.cpp b/test/io/test_ply_io.cpp new file mode 100644 index 00000000..b169dbe4 --- /dev/null +++ b/test/io/test_ply_io.cpp @@ -0,0 +1,482 @@ +/* + * Software License Agreement (BSD License) + * + * Point Cloud Library (PCL) - www.pointclouds.org + * Copyright (c) 2017-, Open Perception, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the copyright holder(s) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#include +#include +#include +#include +#include +#include +#include + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +TEST (PCL, PLYReaderWriter) +{ + using pcl::PointXYZI; + using pcl::PointXYZ; + + pcl::PCLPointCloud2 cloud_blob, cloud_blob2; + pcl::PointCloud cloud, cloud2; + + cloud.width = 640; + cloud.height = 480; + cloud.resize (cloud.width * cloud.height); + cloud.is_dense = true; + + srand (static_cast (time (NULL))); + size_t nr_p = cloud.size (); + // Randomly create a new point cloud + for (size_t i = 0; i < nr_p; ++i) + { + cloud[i].x = static_cast (1024 * rand () / (RAND_MAX + 1.0)); + cloud[i].y = static_cast (1024 * rand () / (RAND_MAX + 1.0)); + cloud[i].z = static_cast (1024 * rand () / (RAND_MAX + 1.0)); + cloud[i].intensity = static_cast (i); + } + + // Convert from data type to blob + toPCLPointCloud2 (cloud, cloud_blob); + + EXPECT_EQ (cloud_blob.width, cloud.width); // test for toPCLPointCloud2 () + EXPECT_EQ (cloud_blob.height, cloud.height); // test for toPCLPointCloud2 () + EXPECT_EQ (cloud_blob.is_dense, cloud.is_dense); // test for toPCLPointCloud2 () + EXPECT_EQ (cloud_blob.data.size (), + cloud_blob.width * cloud_blob.height * sizeof (PointXYZI)); + + // test for toPCLPointCloud2 () + pcl::PLYWriter writer; + writer.write ("test_pcl_io.ply", cloud_blob, Eigen::Vector4f::Zero (), Eigen::Quaternionf::Identity (), true, true); + + pcl::PLYReader reader; + reader.read ("test_pcl_io.ply", cloud_blob2); + //PLY DOES preserve organiziation + EXPECT_EQ (cloud_blob.width * cloud_blob.height, cloud_blob2.width * cloud_blob2.height); + EXPECT_EQ (cloud_blob.is_dense, cloud.is_dense); + EXPECT_EQ (size_t (cloud_blob2.data.size ()), // PointXYZI is 16*2 (XYZ+1, Intensity+3) + cloud_blob2.width * cloud_blob2.height * sizeof (PointXYZ)); // test for loadPLYFile () + + // Convert from blob to data type + fromPCLPointCloud2 (cloud_blob2, cloud2); + + // EXPECT_EQ (cloud.width, cloud2.width); // test for fromPCLPointCloud2 () + // EXPECT_EQ (cloud.height, cloud2.height); // test for fromPCLPointCloud2 () + EXPECT_EQ (cloud.is_dense, cloud2.is_dense); // test for fromPCLPointCloud2 () + EXPECT_EQ (cloud.size (), cloud2.size ()); // test for fromPCLPointCloud2 () + + for (uint32_t counter = 0; counter < cloud.size (); ++counter) + { + EXPECT_FLOAT_EQ (cloud[counter].x, cloud2[counter].x); // test for fromPCLPointCloud2 () + EXPECT_FLOAT_EQ (cloud[counter].y, cloud2[counter].y); // test for fromPCLPointCloud2 () + EXPECT_FLOAT_EQ (cloud[counter].z, cloud2[counter].z); // test for fromPCLPointCloud2 () + EXPECT_FLOAT_EQ (cloud[counter].intensity, cloud2[counter].intensity); // test for fromPCLPointCloud2 () + } +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct PLYTest : public ::testing::Test +{ + PLYTest () : mesh_file_ply_("ply_file.ply") + {} + + virtual + ~PLYTest () { remove (mesh_file_ply_.c_str ()); } + + std::string mesh_file_ply_; +}; + +struct PLYColorTest : public PLYTest +{ + void SetUp () + { + // Test colors from ply_benchmark.ply + clr_1_.r = 255; + clr_1_.g = 0; + clr_1_.b = 0; + clr_1_.a = 255; + + clr_2_.r = 0; + clr_2_.g = 255; + clr_2_.b = 0; + clr_2_.a = 0; + + clr_3_.r = 0; + clr_3_.g = 0; + clr_3_.b = 255; + clr_3_.a = 128; + + clr_4_.r = 255; + clr_4_.g = 255; + clr_4_.b = 255; + clr_4_.a = 128; + + std::ofstream fs; + fs.open (mesh_file_ply_.c_str ()); + fs << "ply\n" + "format ascii 1.0\n" + "element vertex 4\n" + "property float x\n" + "property float y\n" + "property float z\n" + "property uchar red\n" + "property uchar green\n" + "property uchar blue\n" + "property uchar alpha\n" + "element face 2\n" + "property list uchar int vertex_indices\n" + "end_header\n" + "4.23607 0 1.61803 " + << unsigned (clr_1_.r) << ' ' + << unsigned (clr_1_.g) << ' ' + << unsigned (clr_1_.b) << ' ' + << unsigned (clr_1_.a) << "\n" + "2.61803 2.61803 2.61803 " + << unsigned (clr_2_.r) << ' ' + << unsigned (clr_2_.g) << ' ' + << unsigned (clr_2_.b) << ' ' + << unsigned (clr_2_.a) << "\n" + "0 1.61803 4.23607 " + << unsigned (clr_3_.r) << ' ' + << unsigned (clr_3_.g) << ' ' + << unsigned (clr_3_.b) << ' ' + << unsigned (clr_3_.a) << "\n" + "0 -1.61803 4.23607 " + << unsigned (clr_4_.r) << ' ' + << unsigned (clr_4_.g) << ' ' + << unsigned (clr_4_.b) << ' ' + << unsigned (clr_4_.a) << "\n" + "3 0 1 2\n" + "3 1 2 3\n"; + fs.close (); + } + + pcl::RGB clr_1_; + pcl::RGB clr_2_; + pcl::RGB clr_3_; + pcl::RGB clr_4_; +}; + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +TEST_F (PLYColorTest, LoadPLYFileColoredASCIIIntoBlob) +{ + int res; + uint32_t rgba; + + pcl::PCLPointCloud2 cloud_blob; + uint32_t ps; + int32_t offset = -1; + + // check if loading is ok + res = pcl::io::loadPLYFile (mesh_file_ply_, cloud_blob); + ASSERT_EQ (res, 0); + + // blob has proper structure + EXPECT_EQ (cloud_blob.height, 1); + EXPECT_EQ (cloud_blob.width, 4); + EXPECT_EQ (cloud_blob.fields.size(), 4); + EXPECT_FALSE (cloud_blob.is_bigendian); + EXPECT_EQ (cloud_blob.point_step, 16); + EXPECT_EQ (cloud_blob.row_step, 16 * 4); + EXPECT_EQ (cloud_blob.data.size(), 16 * 4); + EXPECT_TRUE (cloud_blob.is_dense); + + // scope blob data + ps = cloud_blob.point_step; + for (size_t i = 0; i < cloud_blob.fields.size (); ++i) + if (cloud_blob.fields[i].name == std::string("rgba")) + offset = static_cast (cloud_blob.fields[i].offset); + + ASSERT_GE (offset, 0); + + // 1st point + rgba = *reinterpret_cast (&cloud_blob.data[offset]); + ASSERT_EQ (rgba, clr_1_.rgba); + + // 2th point + rgba = *reinterpret_cast (&cloud_blob.data[ps + offset]); + ASSERT_EQ (rgba, clr_2_.rgba); + + // 3th point + rgba = *reinterpret_cast (&cloud_blob.data[2 * ps + offset]); + ASSERT_EQ (rgba, clr_3_.rgba); + + // 4th point + rgba = *reinterpret_cast (&cloud_blob.data[3 * ps + offset]); + ASSERT_EQ (rgba, clr_4_.rgba); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +TEST_F (PLYColorTest, LoadPLYFileColoredASCIIIntoPolygonMesh) +{ + int res; + uint32_t rgba; + pcl::PolygonMesh mesh; + uint32_t ps; + int32_t offset = -1; + + // check if loading is ok + res = pcl::io::loadPLYFile (mesh_file_ply_, mesh); + ASSERT_EQ (res, 0); + + // blob has proper structure + EXPECT_EQ (mesh.cloud.height, 1); + EXPECT_EQ (mesh.cloud.width, 4); + EXPECT_EQ (mesh.cloud.fields.size(), 4); + EXPECT_FALSE (mesh.cloud.is_bigendian); + EXPECT_EQ (mesh.cloud.point_step, 16); + EXPECT_EQ (mesh.cloud.row_step, 16 * 4); + EXPECT_EQ (mesh.cloud.data.size(), 16 * 4); + EXPECT_TRUE (mesh.cloud.is_dense); + + // scope blob data + ps = mesh.cloud.point_step; + for (size_t i = 0; i < mesh.cloud.fields.size (); ++i) + if (mesh.cloud.fields[i].name == std::string("rgba")) + offset = static_cast (mesh.cloud.fields[i].offset); + + ASSERT_GE (offset, 0); + + // 1st point + rgba = *reinterpret_cast (&mesh.cloud.data[offset]); + ASSERT_EQ (rgba, clr_1_.rgba); + + // 2th point + rgba = *reinterpret_cast (&mesh.cloud.data[ps + offset]); + ASSERT_EQ (rgba, clr_2_.rgba); + + // 3th point + rgba = *reinterpret_cast (&mesh.cloud.data[2 * ps + offset]); + ASSERT_EQ (rgba, clr_3_.rgba); + + // 4th point + rgba = *reinterpret_cast (&mesh.cloud.data[3 * ps + offset]); + ASSERT_EQ (rgba, clr_4_.rgba); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +template class PLYPointCloudTest : public PLYColorTest { }; +typedef ::testing::Types RGBPointTypes; +TYPED_TEST_CASE (PLYPointCloudTest, RGBPointTypes); +TYPED_TEST (PLYPointCloudTest, LoadPLYFileColoredASCIIIntoPointCloud) +{ + int res; + pcl::PointCloud cloud_rgb; + + // check if loading is ok + res = pcl::io::loadPLYFile (PLYTest::mesh_file_ply_, cloud_rgb); + ASSERT_EQ (res, 0); + + // cloud has proper structure + EXPECT_EQ (cloud_rgb.height, 1); + EXPECT_EQ (cloud_rgb.width, 4); + EXPECT_EQ (cloud_rgb.points.size(), 4); + EXPECT_TRUE (cloud_rgb.is_dense); + + // scope cloud data + ASSERT_EQ (cloud_rgb[0].rgba, PLYColorTest::clr_1_.rgba); + ASSERT_EQ (cloud_rgb[1].rgba, PLYColorTest::clr_2_.rgba); + ASSERT_EQ (cloud_rgb[2].rgba, PLYColorTest::clr_3_.rgba); + ASSERT_EQ (cloud_rgb[3].rgba, PLYColorTest::clr_4_.rgba); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +template +struct PLYCoordinatesIsDenseTest : public PLYTest {}; + +typedef ::testing::Types XYZPointTypes; +TYPED_TEST_CASE (PLYCoordinatesIsDenseTest, XYZPointTypes); + +TYPED_TEST (PLYCoordinatesIsDenseTest, NaNInCoordinates) +{ + // create file + std::ofstream fs; + fs.open (PLYTest::mesh_file_ply_.c_str ()); + fs << "ply\n" + "format ascii 1.0\n" + "element vertex 4\n" + "property float x\n" + "property float y\n" + "property float z\n" + "end_header\n" + "4.23607 NaN 1.61803 \n" + "2.61803 2.61803 2.61803 \n" + "0 1.61803 4.23607 \n" + "0 -1.61803 4.23607 \n"; + fs.close (); + + // Set up cloud + pcl::PointCloud cloud; + + // check if loading is ok + const int res = pcl::io::loadPLYFile (PLYTest::mesh_file_ply_, cloud); + ASSERT_EQ (res, 0); + + // cloud has proper structure + EXPECT_FALSE (cloud.is_dense); +} + +TYPED_TEST (PLYCoordinatesIsDenseTest, nanInCoordinates) +{ + // create file + std::ofstream fs; + fs.open (PLYTest::mesh_file_ply_.c_str ()); + fs << "ply\n" + "format ascii 1.0\n" + "element vertex 4\n" + "property float x\n" + "property float y\n" + "property float z\n" + "end_header\n" + "4.23607 0 1.61803 \n" + "2.61803 2.61803 2.61803 \n" + "nan 1.61803 4.23607 \n" + "0 -1.61803 4.23607 \n"; + fs.close (); + + // Set up cloud + pcl::PointCloud cloud; + + // check if loading is ok + const int res = pcl::io::loadPLYFile (PLYTest::mesh_file_ply_, cloud); + ASSERT_EQ (res, 0); + + // cloud has proper structure + EXPECT_FALSE (cloud.is_dense); +} + +TYPED_TEST (PLYCoordinatesIsDenseTest, InfInCoordinates) +{ + // create file + std::ofstream fs; + fs.open (PLYTest::mesh_file_ply_.c_str ()); + fs << "ply\n" + "format ascii 1.0\n" + "element vertex 4\n" + "property float x\n" + "property float y\n" + "property float z\n" + "end_header\n" + "4.23607 0 1.61803 \n" + "2.61803 2.61803 Inf \n" + "0 1.61803 4.23607 \n" + "0 -1.61803 4.23607 \n"; + fs.close (); + + // Set up cloud + pcl::PointCloud cloud; + + // check if loading is ok + const int res = pcl::io::loadPLYFile (PLYTest::mesh_file_ply_, cloud); + ASSERT_EQ (res, 0); + + // cloud has proper structure + EXPECT_FALSE (cloud.is_dense); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +template +struct PLYNormalsIsDenseTest : public PLYTest {}; + +typedef ::testing::Types NormalPointTypes; +TYPED_TEST_CASE (PLYNormalsIsDenseTest, NormalPointTypes); + +TYPED_TEST (PLYNormalsIsDenseTest, NaNInNormals) +{ + // create file + std::ofstream fs; + fs.open (PLYTest::mesh_file_ply_.c_str ()); + fs << "ply\n" + "format ascii 1.0\n" + "element vertex 4\n" + "property float normal_x\n" + "property float normal_y\n" + "property float normal_z\n" + "end_header\n" + "4.23607 0 1.61803 \n" + "2.61803 2.61803 NaN \n" + "0 1.61803 4.23607 \n" + "0 -1.61803 4.23607 \n"; + fs.close (); + + // Set up cloud + pcl::PointCloud cloud; + + // check if loading is ok + const int res = pcl::io::loadPLYFile (PLYTest::mesh_file_ply_, cloud); + ASSERT_EQ (res, 0); + + // cloud has proper structure + EXPECT_FALSE (cloud.is_dense); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +TEST_F (PLYTest, NaNInIntensity) +{ + // create file + std::ofstream fs; + fs.open (mesh_file_ply_.c_str ()); + fs << "ply\n" + "format ascii 1.0\n" + "element vertex 4\n" + "property float x\n" + "property float y\n" + "property float z\n" + "property float intensity\n" + "end_header\n" + "4.23607 0 1.61803 3.13223\n" + "2.61803 2.61803 0 3.13223\n" + "0 1.61803 4.23607 NaN\n" + "0 -1.61803 4.23607 3.13223\n"; + fs.close (); + + // Set up cloud + pcl::PointCloud cloud; + + // check if loading is ok + const int res = pcl::io::loadPLYFile (PLYTest::mesh_file_ply_, cloud); + ASSERT_EQ (res, 0); + + // cloud has proper structure + EXPECT_FALSE (cloud.is_dense); +} + +/* ---[ */ +int +main (int argc, char** argv) +{ + testing::InitGoogleTest (&argc, argv); + return (RUN_ALL_TESTS ()); +} +/* ]--- */ diff --git a/test/io/test_ply_mesh_io.cpp b/test/io/test_ply_mesh_io.cpp index f16da63a..0bb613e9 100644 --- a/test/io/test_ply_mesh_io.cpp +++ b/test/io/test_ply_mesh_io.cpp @@ -116,6 +116,9 @@ TEST (PCL, PLYPolygonMeshIO) EXPECT_EQ (mesh_binary_pcl.polygons[i].vertices[j], mesh.polygons[i].vertices[j]); } } + + remove ("test_mesh_ascii.ply"); + remove ("test_mesh_binary.ply"); } TEST (PCL, PLYPolygonMeshColoredIO) @@ -276,6 +279,11 @@ TEST (PCL, PLYPolygonMeshColoredIO) EXPECT_EQ (mesh_rgba_binary_pcl.polygons[i].vertices[j], mesh.polygons[i].vertices[j]); } } + + remove ("test_mesh_rgb_ascii.ply"); + remove ("test_mesh_rgba_ascii.ply"); + remove ("test_mesh_rgb_binary.ply"); + remove ("test_mesh_rgba_binary.ply"); } /* ---[ */ diff --git a/test/kdtree/test_kdtree.cpp b/test/kdtree/test_kdtree.cpp index c051e589..1d35b66c 100644 --- a/test/kdtree/test_kdtree.cpp +++ b/test/kdtree/test_kdtree.cpp @@ -64,7 +64,7 @@ struct MyPoint : public PointXYZ PointCloud cloud, cloud_big; -// Includ the implementation so that KdTree works +// Include the implementation so that KdTree works #include void diff --git a/test/octree/CMakeLists.txt b/test/octree/CMakeLists.txt index a1c8fe73..830ccd94 100644 --- a/test/octree/CMakeLists.txt +++ b/test/octree/CMakeLists.txt @@ -11,4 +11,7 @@ if (build) PCL_ADD_TEST(a_octree_test test_octree FILES test_octree.cpp LINK_WITH pcl_gtest pcl_common) + PCL_ADD_TEST(a_octree_iterator_test test_octree_iterator + FILES test_octree_iterator.cpp + LINK_WITH pcl_gtest pcl_common pcl_octree) endif (build) diff --git a/test/octree/test_octree.cpp b/test/octree/test_octree.cpp index 2c7b9fa5..d9a1f4c8 100644 --- a/test/octree/test_octree.cpp +++ b/test/octree/test_octree.cpp @@ -295,9 +295,9 @@ TEST (PCL, Octree_Dynamic_Depth_Test) unsigned int leaf_node_counter = 0; // iterate over tree - OctreePointCloudPointVector::LeafNodeIterator it2; - const OctreePointCloudPointVector::LeafNodeIterator it2_end = octree.leaf_end(); - for (it2 = octree.leaf_begin(); it2 != it2_end; ++it2) + OctreePointCloudPointVector::LeafNodeDepthFirstIterator it2; + const OctreePointCloudPointVector::LeafNodeDepthFirstIterator it2_end = octree.leaf_depth_end(); + for (it2 = octree.leaf_depth_begin(); it2 != it2_end; ++it2) { ++leaf_node_counter; unsigned int depth = it2.getCurrentOctreeDepth (); @@ -326,14 +326,14 @@ TEST (PCL, Octree_Dynamic_Depth_Test) octree.addPointsFromInputCloud (); // test iterator - OctreePointCloudPointVector::LeafNodeIterator it; - const OctreePointCloudPointVector::LeafNodeIterator it_end = octree.leaf_end(); + OctreePointCloudPointVector::LeafNodeDepthFirstIterator it; + const OctreePointCloudPointVector::LeafNodeDepthFirstIterator it_end = octree.leaf_depth_end(); unsigned int leaf_count = 0; std::vector indexVector; // iterate over tree - for (it = octree.leaf_begin(); it != it_end; ++it) + for (it = octree.leaf_depth_begin(); it != it_end; ++it) { OctreeNode* node = it.getCurrentOctreeNode (); @@ -902,13 +902,13 @@ TEST (PCL, Octree_Pointcloud_Iterator_Test) octreeA.addPointsFromInputCloud (); // instantiate iterator for octreeA - OctreePointCloud::LeafNodeIterator it1; - OctreePointCloud::LeafNodeIterator it1_end = octreeA.leaf_end(); + OctreePointCloud::LeafNodeDepthFirstIterator it1; + OctreePointCloud::LeafNodeDepthFirstIterator it1_end = octreeA.leaf_depth_end(); std::vector indexVector; unsigned int leafNodeCounter = 0; - for (it1 = octreeA.leaf_begin(); it1 != it1_end; ++it1) + for (it1 = octreeA.leaf_depth_begin(); it1 != it1_end; ++it1) { it1.getLeafContainer().getPointIndices(indexVector); leafNodeCounter++; @@ -1335,9 +1335,9 @@ TEST (PCL, Octree_Pointcloud_Box_Search) bool idxInResults; const PointXYZ& pt = cloudIn->points[i]; - inBox = (pt.x > lowerBoxCorner (0)) && (pt.x < upperBoxCorner (0)) && - (pt.y > lowerBoxCorner (1)) && (pt.y < upperBoxCorner (1)) && - (pt.z > lowerBoxCorner (2)) && (pt.z < upperBoxCorner (2)); + inBox = (pt.x >= lowerBoxCorner (0)) && (pt.x <= upperBoxCorner (0)) && + (pt.y >= lowerBoxCorner (1)) && (pt.y <= upperBoxCorner (1)) && + (pt.z >= lowerBoxCorner (2)) && (pt.z <= upperBoxCorner (2)); idxInResults = false; for (j = 0; (j < k_indices.size ()) && (!idxInResults); ++j) diff --git a/test/octree/test_octree_iterator.cpp b/test/octree/test_octree_iterator.cpp new file mode 100644 index 00000000..08fc40a8 --- /dev/null +++ b/test/octree/test_octree_iterator.cpp @@ -0,0 +1,1886 @@ +/* + * Software License Agreement (BSD License) + * + * Point Cloud Library (PCL) - www.pointclouds.org + * Copyright (c) 2017-, Open Perception, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the copyright holder(s) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * + */ +#include +#include +#include +#include +#include +#include + +using pcl::octree::OctreeBase; +using pcl::octree::OctreeIteratorBase; +using pcl::octree::OctreeKey; + +//////////////////////////////////////////////////////// +// OctreeIteratorBase +//////////////////////////////////////////////////////// + +struct OctreeIteratorBaseTest : public testing::Test +{ + // types + typedef OctreeBase OctreeBaseT; + typedef OctreeIteratorBase OctreeIteratorBaseT; + + + // methods + virtual void SetUp () + { + octree_.setTreeDepth (2); //can have at most 8^2 leaves + } + + // members + OctreeBaseT octree_; +}; + +TEST_F (OctreeIteratorBaseTest, CopyConstructor) +{ + OctreeIteratorBaseT it_a; + OctreeIteratorBaseT it_b (&octree_, 0); + OctreeIteratorBaseT it_c (it_b); //Our copy constructor + + EXPECT_NE (it_a, it_c); + EXPECT_EQ (it_b, it_c); +} + +TEST_F (OctreeIteratorBaseTest, CopyAssignment) +{ + OctreeIteratorBaseT it_a; + OctreeIteratorBaseT it_b (&octree_, 0); + OctreeIteratorBaseT it_c; + + EXPECT_EQ (it_a, it_c); + EXPECT_NE (it_b, it_c); + + it_c = it_a; //Our copy assignment + EXPECT_EQ (it_a, it_c); + EXPECT_NE (it_b, it_c); + + it_c = it_b; //Our copy assignment + EXPECT_NE (it_a, it_c); + EXPECT_EQ (it_b, it_c); +} + +//////////////////////////////////////////////////////// +// Iterator fixture setup +//////////////////////////////////////////////////////// + +template +struct OctreeIteratorTest : public OctreeIteratorBaseTest +{ + // types + typedef OctreeKey OctreeKeyT; + + // methods + OctreeIteratorTest () : it_ (&octree_, tree_depth_) {} + + virtual void SetUp () + { + // Set up my octree + octree_.setTreeDepth (tree_depth_); //can have at most 8 leaves + + // Generate the unique key for our leaves + keys_[0] = OctreeKeyT (0b0u, 0b0u, 0b0u); + keys_[1] = OctreeKeyT (0b0u, 0b0u, 0b1u); + keys_[2] = OctreeKeyT (0b0u, 0b1u, 0b0u); + keys_[3] = OctreeKeyT (0b0u, 0b1u, 0b1u); + keys_[4] = OctreeKeyT (0b1u, 0b0u, 0b0u); + keys_[5] = OctreeKeyT (0b1u, 0b0u, 0b1u); + keys_[6] = OctreeKeyT (0b1u, 0b1u, 0b0u); + keys_[7] = OctreeKeyT (0b1u, 0b1u, 0b1u); + + // Create the leaves + for (uint8_t i = 0; i < 8; ++i) + octree_.createLeaf (keys_[i].x, keys_[i].y, keys_[i].z); + + // reset the iterator state + it_.reset (); + + // increment the iterator 4 times + for (uint8_t i = 0; i < 4; ++it_, ++i); + } + + // members + const static unsigned tree_depth_ = 1; + + OctreeKeyT keys_[8]; + + T it_; +}; + +using pcl::octree::OctreeDepthFirstIterator; +using pcl::octree::OctreeBreadthFirstIterator; +using pcl::octree::OctreeLeafNodeDepthFirstIterator; +using pcl::octree::OctreeFixedDepthIterator; +using pcl::octree::OctreeLeafNodeBreadthFirstIterator; + +typedef testing::Types >, + OctreeBreadthFirstIterator >, + OctreeLeafNodeDepthFirstIterator >, + OctreeFixedDepthIterator >, + OctreeLeafNodeBreadthFirstIterator > > OctreeIteratorTypes; +TYPED_TEST_CASE (OctreeIteratorTest, OctreeIteratorTypes); + +TYPED_TEST (OctreeIteratorTest, CopyConstructor) +{ + TypeParam it_a; + TypeParam it_b (this->it_); // copy ctor + + EXPECT_NE (it_a, it_b); + EXPECT_EQ (this->it_, it_b); + EXPECT_EQ (*this->it_, *it_b); + EXPECT_EQ (this->it_.getNodeID (), it_b.getNodeID ()); + EXPECT_EQ (this->it_ == it_b, !(this->it_ != it_b)); + + EXPECT_EQ (this->it_.getCurrentOctreeKey (), it_b.getCurrentOctreeKey ()); + EXPECT_EQ (this->it_.getCurrentOctreeDepth (), it_b.getCurrentOctreeDepth ()); + EXPECT_EQ (this->it_.getCurrentOctreeNode (), it_b.getCurrentOctreeNode ()); + + EXPECT_EQ (this->it_.isBranchNode (), it_b.isBranchNode ()); + EXPECT_EQ (this->it_.isLeafNode (), it_b.isLeafNode ()); + + EXPECT_EQ (this->it_.getNodeConfiguration (), it_b.getNodeConfiguration ()); + + EXPECT_EQ (this->it_.getNodeID (), it_b.getNodeID ()); +} + +TYPED_TEST (OctreeIteratorTest, CopyAssignment) +{ + TypeParam it_a; + TypeParam it_b (this->it_); // copy ctor + TypeParam it_c; + + // Don't modify it + EXPECT_EQ (it_a, it_c); + EXPECT_NE (it_b, it_c); + + it_c = it_b; //Our copy assignment + EXPECT_NE (it_a, it_c); + EXPECT_EQ (it_b, it_c); + EXPECT_EQ (*it_b, *it_c); + EXPECT_EQ (it_b.getNodeID (), it_c.getNodeID ()); + EXPECT_EQ (it_b == it_c, !(it_b != it_c)); + + it_c = it_a; //Our copy assignment + EXPECT_EQ (it_a, it_c); + EXPECT_NE (it_b, it_c); +} + +//////////////////////////////////////////////////////// +// OctreeBase Begin/End Iterator Construction +//////////////////////////////////////////////////////// + +struct OctreeBaseBeginEndIteratorsTest : public testing::Test +{ + // Types + typedef OctreeBase OctreeT; + + // Methods + void SetUp () + { + // Set tree depth + oct_a_.setTreeDepth (2); + oct_b_.setTreeDepth (2); + + // Populate leaves 8^2 = 64 uids + // 64 needs 6 bits in total spread among 3 keys (x, y and z). + // 2 bits per key + // The 3 LSBs of the id match the 1 LSB of the x, y and z keys + // The 3 MSBs of the id match the 1 MSB of the x, y and z keys + for (size_t i = 0; i < 64u; ++i) + { + const OctreeKey key (((i >> 4) & 0b10u) | ((i >> 2) & 1u), // x + ((i >> 3) & 0b10u) | ((i >> 1) & 1u), // y + ((i >> 2) & 0b10u) | (i & 1u));// z + oct_a_.createLeaf (key.x, key.y, key.z); + oct_b_.createLeaf (key.x, key.y, key.z); + } + } + + // Members + OctreeT oct_a_, oct_b_; +}; + +TEST_F (OctreeBaseBeginEndIteratorsTest, Begin) +{ + // Useful types + typedef typename OctreeT::Iterator IteratorT; + + // Default initialization + IteratorT it_a_1 = oct_a_.begin (); + IteratorT it_a_2 = oct_a_.begin (); + IteratorT it_b = oct_b_.begin (); + + EXPECT_EQ (it_a_1, it_a_2); + EXPECT_NE (it_a_1, it_b); + EXPECT_NE (it_a_2, it_b); + + // Different max depths are not the same iterators + IteratorT it_m = oct_a_.begin (); + IteratorT it_m_1 = oct_a_.begin (1); + IteratorT it_m_2 = oct_a_.begin (2); + IteratorT it_m_b_1 = oct_b_.begin (1); + + EXPECT_NE (it_m_1, it_m_2); + EXPECT_EQ (it_m_2, it_m); // tree depth is 2 + EXPECT_NE (it_m_1, it_m_b_1); +} + +TEST_F (OctreeBaseBeginEndIteratorsTest, End) +{ + // Useful types + typedef typename OctreeT::Iterator IteratorT; + + // Default initialization + IteratorT it_a_1 = oct_a_.end (); + IteratorT it_a_2 = oct_a_.end (); + IteratorT it_b = oct_b_.end (); + + EXPECT_EQ (it_a_1, it_a_2); + EXPECT_NE (it_a_1, it_b); + EXPECT_NE (it_a_2, it_b); +} + +TEST_F (OctreeBaseBeginEndIteratorsTest, LeafBegin) +{ + // Useful types + typedef typename OctreeT::LeafNodeDepthFirstIterator IteratorT; + + // Default initialization + IteratorT it_a_1 = oct_a_.leaf_depth_begin (); + IteratorT it_a_2 = oct_a_.leaf_depth_begin (); + IteratorT it_b = oct_b_.leaf_depth_begin (); + + EXPECT_EQ (it_a_1, it_a_2); + EXPECT_NE (it_a_1, it_b); + EXPECT_NE (it_a_2, it_b); + + // Different max depths are not the same iterators + IteratorT it_m = oct_a_.leaf_depth_begin (); + IteratorT it_m_1 = oct_a_.leaf_depth_begin (1); + IteratorT it_m_2 = oct_a_.leaf_depth_begin (2); + IteratorT it_m_b_1 = oct_b_.leaf_depth_begin (1); + + EXPECT_NE (it_m_1, it_m_2); + EXPECT_EQ (it_m_2, it_m); // tree depth is 2 + EXPECT_NE (it_m_1, it_m_b_1); +} + +TEST_F (OctreeBaseBeginEndIteratorsTest, LeafEnd) +{ + // Useful types + typedef typename OctreeT::LeafNodeDepthFirstIterator IteratorT; + + // Default initialization + IteratorT it_a_1 = oct_a_.leaf_depth_end (); + IteratorT it_a_2 = oct_a_.leaf_depth_end (); + IteratorT it_b = oct_b_.leaf_depth_end (); + + EXPECT_EQ (it_a_1, it_a_2); + EXPECT_NE (it_a_1, it_b); + EXPECT_NE (it_a_2, it_b); +} + +TEST_F (OctreeBaseBeginEndIteratorsTest, DepthBegin) +{ + // Useful types + typedef typename OctreeT::DepthFirstIterator IteratorT; + + // Default initialization + IteratorT it_a_1 = oct_a_.depth_begin (); + IteratorT it_a_2 = oct_a_.depth_begin (); + IteratorT it_b = oct_b_.depth_begin (); + + EXPECT_EQ (it_a_1, it_a_2); + EXPECT_NE (it_a_1, it_b); + EXPECT_NE (it_a_2, it_b); + + // Different max depths are not the same iterators + IteratorT it_m = oct_a_.depth_begin (); + IteratorT it_m_1 = oct_a_.depth_begin (1); + IteratorT it_m_2 = oct_a_.depth_begin (2); + IteratorT it_m_b_1 = oct_b_.depth_begin (1); + + EXPECT_NE (it_m_1, it_m_2); + EXPECT_EQ (it_m_2, it_m); // tree depth is 2 + EXPECT_NE (it_m_1, it_m_b_1); +} + +TEST_F (OctreeBaseBeginEndIteratorsTest, DepthEnd) +{ + // Useful types + typedef typename OctreeT::DepthFirstIterator IteratorT; + + // Default initialization + IteratorT it_a_1 = oct_a_.depth_end (); + IteratorT it_a_2 = oct_a_.depth_end (); + IteratorT it_b = oct_b_.depth_end (); + + EXPECT_EQ (it_a_1, it_a_2); + EXPECT_NE (it_a_1, it_b); + EXPECT_NE (it_a_2, it_b); +} + +TEST_F (OctreeBaseBeginEndIteratorsTest, BreadthBegin) +{ + // Useful types + typedef typename OctreeT::BreadthFirstIterator IteratorT; + + // Default initialization + IteratorT it_a_1 = oct_a_.breadth_begin (); + IteratorT it_a_2 = oct_a_.breadth_begin (); + IteratorT it_b = oct_b_.breadth_begin (); + + EXPECT_EQ (it_a_1, it_a_2); + EXPECT_NE (it_a_1, it_b); + EXPECT_NE (it_a_2, it_b); + + // Different max depths are not the same iterators + IteratorT it_m = oct_a_.breadth_begin (); + IteratorT it_m_1 = oct_a_.breadth_begin (1); + IteratorT it_m_2 = oct_a_.breadth_begin (2); + IteratorT it_m_b_1 = oct_b_.breadth_begin (1); + + EXPECT_NE (it_m_1, it_m_2); + EXPECT_EQ (it_m_2, it_m); // tree depth is 2 + EXPECT_NE (it_m_1, it_m_b_1); +} + +TEST_F (OctreeBaseBeginEndIteratorsTest, BreadthEnd) +{ + // Useful types + typedef typename OctreeT::BreadthFirstIterator IteratorT; + + // Default initialization + IteratorT it_a_1 = oct_a_.breadth_end (); + IteratorT it_a_2 = oct_a_.breadth_end (); + IteratorT it_b = oct_b_.breadth_end (); + + EXPECT_EQ (it_a_1, it_a_2); + EXPECT_NE (it_a_1, it_b); + EXPECT_NE (it_a_2, it_b); +} + +TEST_F (OctreeBaseBeginEndIteratorsTest, LeafBreadthBegin) +{ + // Useful types + typedef typename OctreeT::LeafNodeBreadthFirstIterator IteratorT; + + // Default initialization + IteratorT it_a_1 = oct_a_.leaf_breadth_begin (); + IteratorT it_a_2 = oct_a_.leaf_breadth_begin (); + IteratorT it_b = oct_b_.leaf_breadth_begin (); + + EXPECT_EQ (it_a_1, it_a_2); + EXPECT_NE (it_a_1, it_b); + EXPECT_NE (it_a_2, it_b); + + // Different max depths are not the same iterators + IteratorT it_m = oct_a_.leaf_breadth_begin (); + IteratorT it_m_1 = oct_a_.leaf_breadth_begin (1); + IteratorT it_m_2 = oct_a_.leaf_breadth_begin (2); + IteratorT it_m_b_1 = oct_b_.leaf_breadth_begin (1); + + EXPECT_NE (it_m_1, it_m_2); + EXPECT_EQ (it_m_2, it_m); // tree depth is 2 + EXPECT_NE (it_m_1, it_m_b_1); +} + +TEST_F (OctreeBaseBeginEndIteratorsTest, LeafBreadthEnd) +{ + // Useful types + typedef typename OctreeT::LeafNodeBreadthFirstIterator IteratorT; + + // Default initialization + IteratorT it_a_1 = oct_a_.leaf_breadth_end (); + IteratorT it_a_2 = oct_a_.leaf_breadth_end (); + IteratorT it_b = oct_b_.leaf_breadth_end (); + + EXPECT_EQ (it_a_1, it_a_2); + EXPECT_NE (it_a_1, it_b); + EXPECT_NE (it_a_2, it_b); +} + +//////////////////////////////////////////////////////// +// OctreeBase Iterator For Loop Case +//////////////////////////////////////////////////////// + +struct OctreeBaseIteratorsForLoopTest : public OctreeBaseBeginEndIteratorsTest +{ +}; + +TEST_F (OctreeBaseIteratorsForLoopTest, DefaultIterator) +{ + // Useful types + typedef typename OctreeT::Iterator IteratorT; + + // Default initialization + IteratorT it_a; + IteratorT it_a_end = oct_a_.end (); + + unsigned int node_count = 0; + unsigned int branch_count = 0; + unsigned int leaf_count = 0; + + // Iterate over every node of the octree oct_a_. + for (it_a = oct_a_.begin (); it_a != it_a_end; ++it_a) + { + // store node, branch and leaf count + const pcl::octree::OctreeNode* node = it_a.getCurrentOctreeNode (); + if (node->getNodeType () == pcl::octree::BRANCH_NODE) + { + branch_count++; + } + else if (node->getNodeType () == pcl::octree::LEAF_NODE) + { + leaf_count++; + } + node_count++; + } + + // Check the node_count, branch_count and leaf_count values + ASSERT_EQ (leaf_count, 64); + ASSERT_EQ (branch_count, 9); + ASSERT_EQ (branch_count + leaf_count, node_count); + ASSERT_EQ (oct_a_.getLeafCount (), leaf_count); + ASSERT_EQ (oct_a_.getBranchCount (), branch_count); + + // Iterate over the octree oct_a_ with a depth max of 1. + // As oct_a_ has a depth level of 2, we should only iterate + // over 9 branch node: the root node + 8 node at depth 1 + node_count = 0; + branch_count = 0; + leaf_count = 0; + unsigned int max_depth = 1; + for (it_a = oct_a_.begin (max_depth); it_a != it_a_end; ++it_a) + { + // store node, branch and leaf count + const pcl::octree::OctreeNode* node = it_a.getCurrentOctreeNode (); + if (node->getNodeType () == pcl::octree::BRANCH_NODE) + { + branch_count++; + } + else if (node->getNodeType () == pcl::octree::LEAF_NODE) + { + leaf_count++; + } + node_count++; + } + + // Check the node_count, branch_count and leaf_count values + ASSERT_EQ (leaf_count, 0); + ASSERT_EQ (branch_count, 9); + ASSERT_EQ (branch_count + leaf_count, node_count); + ASSERT_EQ (oct_a_.getBranchCount (), branch_count); +} + +TEST_F (OctreeBaseIteratorsForLoopTest, LeafNodeDepthFirstIterator) +{ + // Useful types + typedef typename OctreeT::LeafNodeDepthFirstIterator IteratorT; + + // Default initialization + IteratorT it_a; + IteratorT it_a_end = oct_a_.leaf_depth_end (); + + unsigned int node_count = 0; + unsigned int branch_count = 0; + unsigned int leaf_count = 0; + + // Iterate over every node of the octree oct_a_. + for (it_a = oct_a_.leaf_depth_begin (); it_a != it_a_end; ++it_a) + { + // store node, branch and leaf count + const pcl::octree::OctreeNode* node = it_a.getCurrentOctreeNode (); + if (node->getNodeType () == pcl::octree::BRANCH_NODE) + { + branch_count++; + } + else if (node->getNodeType () == pcl::octree::LEAF_NODE) + { + leaf_count++; + } + node_count++; + } + + // Check the node_count, branch_count and leaf_count values + ASSERT_EQ (leaf_count, 64); + ASSERT_EQ (branch_count, 0); + ASSERT_EQ (oct_a_.getLeafCount (), leaf_count); + + // Iterate over the octree oct_a_ with a depth max of 1. + // As oct_a_ has a depth level of 2, we should only iterate + // over 9 branch node: the root node + 8 node at depth 1 + node_count = 0; + branch_count = 0; + leaf_count = 0; + unsigned int max_depth = 1; + for (it_a = oct_a_.leaf_depth_begin (max_depth); it_a != it_a_end; ++it_a) + { + // store node, branch and leaf count + const pcl::octree::OctreeNode* node = it_a.getCurrentOctreeNode (); + if (node->getNodeType () == pcl::octree::BRANCH_NODE) + { + branch_count++; + } + else if (node->getNodeType () == pcl::octree::LEAF_NODE) + { + leaf_count++; + } + node_count++; + } + + // Check the node_count, branch_count and leaf_count values + ASSERT_EQ (leaf_count, 0); + ASSERT_EQ (branch_count, 0); +} + +TEST_F (OctreeBaseIteratorsForLoopTest, DepthFirstIterator) +{ + // Useful types + typedef typename OctreeT::DepthFirstIterator IteratorT; + + // Default initialization + IteratorT it_a; + IteratorT it_a_end = oct_a_.depth_end (); + + unsigned int node_count = 0; + unsigned int branch_count = 0; + unsigned int leaf_count = 0; + + // Iterate over every node of the octree oct_a_. + for (it_a = oct_a_.depth_begin (); it_a != it_a_end; ++it_a) + { + // store node, branch and leaf count + const pcl::octree::OctreeNode* node = it_a.getCurrentOctreeNode (); + if (node->getNodeType () == pcl::octree::BRANCH_NODE) + { + branch_count++; + } + else if (node->getNodeType () == pcl::octree::LEAF_NODE) + { + leaf_count++; + } + node_count++; + } + + // Check the node_count, branch_count and leaf_count values + ASSERT_EQ (leaf_count, 64); + ASSERT_EQ (branch_count, 9); + ASSERT_EQ (branch_count + leaf_count, node_count); + ASSERT_EQ (oct_a_.getLeafCount (), leaf_count); + ASSERT_EQ (oct_a_.getBranchCount (), branch_count); + + // Iterate over the octree oct_a_ with a depth max of 1. + // As oct_a_ has a depth level of 2, we should only iterate + // over 9 branch node: the root node + 8 node at depth 1 + node_count = 0; + branch_count = 0; + leaf_count = 0; + unsigned int max_depth = 1; + for (it_a = oct_a_.depth_begin (max_depth); it_a != it_a_end; ++it_a) + { + // store node, branch and leaf count + const pcl::octree::OctreeNode* node = it_a.getCurrentOctreeNode (); + if (node->getNodeType () == pcl::octree::BRANCH_NODE) + { + branch_count++; + } + else if (node->getNodeType () == pcl::octree::LEAF_NODE) + { + leaf_count++; + } + node_count++; + } + + // Check the node_count, branch_count and leaf_count values + ASSERT_EQ (leaf_count, 0); + ASSERT_EQ (branch_count, 9); + ASSERT_EQ (branch_count + leaf_count, node_count); + ASSERT_EQ (oct_a_.getBranchCount (), branch_count); +} + +TEST_F (OctreeBaseIteratorsForLoopTest, BreadthFirstIterator) +{ + // Useful types + typedef typename OctreeT::BreadthFirstIterator IteratorT; + + // Default initialization + IteratorT it_a; + IteratorT it_a_end = oct_a_.breadth_end (); + + unsigned int node_count = 0; + unsigned int branch_count = 0; + unsigned int leaf_count = 0; + + // Iterate over every node of the octree oct_a_. + for (it_a = oct_a_.breadth_begin (); it_a != it_a_end; ++it_a) + { + // store node, branch and leaf count + const pcl::octree::OctreeNode* node = it_a.getCurrentOctreeNode (); + if (node->getNodeType () == pcl::octree::BRANCH_NODE) + { + branch_count++; + } + else if (node->getNodeType () == pcl::octree::LEAF_NODE) + { + leaf_count++; + } + node_count++; + } + + // Check the node_count, branch_count and leaf_count values + ASSERT_EQ (leaf_count, 64); + ASSERT_EQ (branch_count, 9); + ASSERT_EQ (branch_count + leaf_count, node_count); + ASSERT_EQ (oct_a_.getLeafCount (), leaf_count); + ASSERT_EQ (oct_a_.getBranchCount (), branch_count); + + // Iterate over the octree oct_a_ with a depth max of 1. + // As oct_a_ has a depth level of 2, we should only iterate + // over 9 branch node: the root node + 8 node at depth 1 + node_count = 0; + branch_count = 0; + leaf_count = 0; + unsigned int max_depth = 1; + for (it_a = oct_a_.breadth_begin (max_depth); it_a != it_a_end; ++it_a) + { + // store node, branch and leaf count + const pcl::octree::OctreeNode* node = it_a.getCurrentOctreeNode (); + if (node->getNodeType () == pcl::octree::BRANCH_NODE) + { + branch_count++; + } + else if (node->getNodeType () == pcl::octree::LEAF_NODE) + { + leaf_count++; + } + node_count++; + } + + // Check the node_count, branch_count and leaf_count values + ASSERT_EQ (leaf_count, 0); + ASSERT_EQ (branch_count, 9); + ASSERT_EQ (branch_count + leaf_count, node_count); + ASSERT_EQ (oct_a_.getBranchCount (), branch_count); +} + +TEST_F (OctreeBaseIteratorsForLoopTest, FixedDepthIterator) +{ + // Useful types + typedef typename OctreeT::FixedDepthIterator IteratorT; + + // Default initialization + IteratorT it_a; + IteratorT it_a_end = oct_a_.fixed_depth_end (); + + unsigned int node_count = 0; + unsigned int branch_count = 0; + unsigned int leaf_count = 0; + + for (unsigned int depth = 0; depth <= oct_a_.getTreeDepth (); ++depth) + { + // Iterate over every node of the octree oct_a_. + for (it_a = oct_a_.fixed_depth_begin (depth); it_a != it_a_end; ++it_a) + { + // store node, branch and leaf count + const pcl::octree::OctreeNode* node = it_a.getCurrentOctreeNode (); + if (node->getNodeType () == pcl::octree::BRANCH_NODE) + { + branch_count++; + } + else if (node->getNodeType () == pcl::octree::LEAF_NODE) + { + leaf_count++; + } + node_count++; + } + } + + // Check the node_count, branch_count and leaf_count values + ASSERT_EQ (leaf_count, 64); + ASSERT_EQ (branch_count, 9); + ASSERT_EQ (branch_count + leaf_count, node_count); + ASSERT_EQ (oct_a_.getLeafCount (), leaf_count); + ASSERT_EQ (oct_a_.getBranchCount (), branch_count); + + // Iterate over the octree oct_a_ with a depth max of 1. + // As oct_a_ has a depth level of 2, we should only iterate + // over 9 branch node: the root node + 8 node at depth 1 + node_count = 0; + branch_count = 0; + leaf_count = 0; + unsigned int fixed_depth = 1; + for (it_a = oct_a_.fixed_depth_begin (fixed_depth); it_a != it_a_end; ++it_a) + { + // store node, branch and leaf count + const pcl::octree::OctreeNode* node = it_a.getCurrentOctreeNode (); + if (node->getNodeType () == pcl::octree::BRANCH_NODE) + { + branch_count++; + } + else if (node->getNodeType () == pcl::octree::LEAF_NODE) + { + leaf_count++; + } + node_count++; + } + + // Check the node_count, branch_count and leaf_count values + ASSERT_EQ (leaf_count, 0); + ASSERT_EQ (branch_count, 8); + ASSERT_EQ (branch_count + leaf_count, node_count); + ASSERT_EQ ((oct_a_.getBranchCount () - 1), branch_count); +} + +TEST_F (OctreeBaseIteratorsForLoopTest, LeafNodeBreadthFirstIterator) +{ + // Useful types + typedef typename OctreeT::LeafNodeBreadthFirstIterator IteratorT; + + // Default initialization + IteratorT it_a; + IteratorT it_a_end = oct_a_.leaf_breadth_end (); + + unsigned int node_count = 0; + unsigned int branch_count = 0; + unsigned int leaf_count = 0; + + // Iterate over every node of the octree oct_a_. + for (it_a = oct_a_.leaf_breadth_begin (); it_a != it_a_end; ++it_a) + { + // store node, branch and leaf count + const pcl::octree::OctreeNode* node = it_a.getCurrentOctreeNode (); + if (node->getNodeType () == pcl::octree::BRANCH_NODE) + { + branch_count++; + } + else if (node->getNodeType () == pcl::octree::LEAF_NODE) + { + leaf_count++; + } + node_count++; + } + + // Check the node_count, branch_count and leaf_count values + ASSERT_EQ (leaf_count, 64); + ASSERT_EQ (branch_count, 0); + ASSERT_EQ (node_count, 64); + ASSERT_EQ (oct_a_.getLeafCount (), leaf_count); + + // Iterate over the octree oct_a_ with a depth max of 1. + // As oct_a_ has a depth level of 2, we should only iterate + // over 9 branch node: the root node + 8 node at depth 1 + node_count = 0; + branch_count = 0; + leaf_count = 0; + unsigned int max_depth = 1; + for (it_a = oct_a_.leaf_breadth_begin (max_depth); it_a != it_a_end; ++it_a) + { + // store node, branch and leaf count + const pcl::octree::OctreeNode* node = it_a.getCurrentOctreeNode (); + if (node->getNodeType () == pcl::octree::BRANCH_NODE) + { + branch_count++; + } + else if (node->getNodeType () == pcl::octree::LEAF_NODE) + { + leaf_count++; + } + node_count++; + } + + // Check the node_count, branch_count and leaf_count values + ASSERT_EQ (leaf_count, 0); + ASSERT_EQ (branch_count, 0); + ASSERT_EQ (node_count, 0); +} + +//////////////////////////////////////////////////////// +// OctreeBase Walk Through Iterator Test +//////////////////////////////////////////////////////// + +struct OctreeBaseWalkThroughIteratorsTest : public testing::Test +{ + // Types + typedef OctreeBase OctreeT; + + // Methods + void SetUp () + { + // Create manually an irregular octree. + // Graphically, this octree appears as follows: + // root + // ' / \ ` + // ' / \ ` + // ' / \ ` + // 000 010 100 110 + // | | + // | | + // 020 220 + // + // The octree key of the different node are represented on this graphic. + // This octree is of max_depth 2. + // At depth 1, you will find: + // - 2 leaf nodes , with the keys 000 and 100, + // - 2 branch nodes, with the keys 010 and 110. + // At depth 2, you will find: + // - 2 leaf nodes , with the keys 000 and 000. + // This octree is build to be able to check the order in which the nodes + // appear depending on the used iterator. + + // Set the leaf nodes at depth 1 + oct_a_.setTreeDepth (1); + + oct_a_.createLeaf (0u, 0u, 0u); + oct_a_.createLeaf (1u, 0u, 0u); + + // Set the leaf nodes at depth 2. As createLeaf method create recursively + // the octree nodes, if the parent node are not present, they will be create. + // In this case, at depth 1, the nodes 010 and 110 are created. + oct_a_.setTreeDepth (2); + + oct_a_.createLeaf (0u, 2u, 0u); + oct_a_.createLeaf (2u, 2u, 0u); + } + + // Members + OctreeT oct_a_; +}; + +TEST_F (OctreeBaseWalkThroughIteratorsTest, LeafNodeDepthFirstIterator) +{ + OctreeT::LeafNodeDepthFirstIterator it = oct_a_.leaf_depth_begin (); + + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (0u, 0u, 0u)); // depth: 1 + EXPECT_EQ (it.getCurrentOctreeDepth (), 1u); + EXPECT_TRUE (it.isLeafNode ()); + ++it; + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (0u, 2u, 0u)); // depth: 2 + EXPECT_EQ (it.getCurrentOctreeDepth (), 2u); + EXPECT_TRUE (it.isLeafNode ()); + ++it; + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (1u, 0u, 0u)); // depth: 1 + EXPECT_EQ (it.getCurrentOctreeDepth (), 1u); + EXPECT_TRUE (it.isLeafNode ()); + ++it; + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (2u, 2u, 0u)); // depth: 2 + EXPECT_EQ (it.getCurrentOctreeDepth (), 2u); + EXPECT_TRUE (it.isLeafNode ()); + ++it; + EXPECT_EQ (it, oct_a_.leaf_depth_end ()); +} + +TEST_F (OctreeBaseWalkThroughIteratorsTest, LeafNodeBreadthFirstIterator) +{ + OctreeT::LeafNodeBreadthFirstIterator it = oct_a_.leaf_breadth_begin (); + + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (0u, 0u, 0u)); // depth: 1 + EXPECT_EQ (it.getCurrentOctreeDepth (), 1u); + EXPECT_TRUE (it.isLeafNode ()); + ++it; + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (1u, 0u, 0u)); // depth: 1 + EXPECT_EQ (it.getCurrentOctreeDepth (), 1u); + EXPECT_TRUE (it.isLeafNode ()); + ++it; + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (0u, 2u, 0u)); // depth: 2 + EXPECT_EQ (it.getCurrentOctreeDepth (), 2u); + EXPECT_TRUE (it.isLeafNode ()); + ++it; + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (2u, 2u, 0u)); // depth: 2 + EXPECT_EQ (it.getCurrentOctreeDepth (), 2u); + EXPECT_TRUE (it.isLeafNode ()); + ++it; + EXPECT_EQ (it, oct_a_.leaf_breadth_end ()); +} + +TEST_F (OctreeBaseWalkThroughIteratorsTest, DepthFirstIterator) +{ + OctreeT::DepthFirstIterator it = oct_a_.depth_begin (); + + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (0u, 0u, 0u)); // depth: 0 + EXPECT_EQ (it.getCurrentOctreeDepth (), 0u); + EXPECT_TRUE (it.isBranchNode ()); + ++it; + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (0u, 0u, 0u)); // depth: 1 + EXPECT_EQ (it.getCurrentOctreeDepth (), 1u); + EXPECT_TRUE (it.isLeafNode ()); + ++it; + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (0u, 1u, 0u)); // depth: 1 + EXPECT_EQ (it.getCurrentOctreeDepth (), 1u); + EXPECT_TRUE (it.isBranchNode ()); + ++it; + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (0u, 2u, 0u)); // depth: 2 + EXPECT_EQ (it.getCurrentOctreeDepth (), 2u); + EXPECT_TRUE (it.isLeafNode ()); + ++it; + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (1u, 0u, 0u)); // depth: 1 + EXPECT_EQ (it.getCurrentOctreeDepth (), 1u); + EXPECT_TRUE (it.isLeafNode ()); + ++it; + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (1u, 1u, 0u)); // depth: 1 + EXPECT_EQ (it.getCurrentOctreeDepth (), 1u); + EXPECT_TRUE (it.isBranchNode ()); + ++it; + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (2u, 2u, 0u)); // depth: 2 + EXPECT_EQ (it.getCurrentOctreeDepth (), 2u); + EXPECT_TRUE (it.isLeafNode ()); + ++it; + EXPECT_EQ (it, oct_a_.depth_end ()); +} + +TEST_F (OctreeBaseWalkThroughIteratorsTest, BreadthFirstIterator) +{ + OctreeT::BreadthFirstIterator it = oct_a_.breadth_begin (); + + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (0u, 0u, 0u)); // depth: 0 + EXPECT_EQ (it.getCurrentOctreeDepth (), 0u); + EXPECT_TRUE (it.isBranchNode ()); + ++it; + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (0u, 0u, 0u)); // depth: 1 + EXPECT_EQ (it.getCurrentOctreeDepth (), 1u); + EXPECT_TRUE (it.isLeafNode ()); + ++it; + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (0u, 1u, 0u)); // depth: 1 + EXPECT_EQ (it.getCurrentOctreeDepth (), 1u); + EXPECT_TRUE (it.isBranchNode ()); + ++it; + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (1u, 0u, 0u)); // depth: 1 + EXPECT_EQ (it.getCurrentOctreeDepth (), 1u); + EXPECT_TRUE (it.isLeafNode ()); + ++it; + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (1u, 1u, 0u)); // depth: 1 + EXPECT_EQ (it.getCurrentOctreeDepth (), 1u); + EXPECT_TRUE (it.isBranchNode ()); + ++it; + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (0u, 2u, 0u)); // depth: 2 + EXPECT_EQ (it.getCurrentOctreeDepth (), 2u); + EXPECT_TRUE (it.isLeafNode ()); + ++it; + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (2u, 2u, 0u)); // depth: 2 + EXPECT_EQ (it.getCurrentOctreeDepth (), 2u); + EXPECT_TRUE (it.isLeafNode ()); + ++it; + EXPECT_EQ (it, oct_a_.breadth_end ()); +} + +TEST_F (OctreeBaseWalkThroughIteratorsTest, FixedDepthIterator) +{ + OctreeT::FixedDepthIterator it; + + // Check the default behavior of the iterator + it = oct_a_.fixed_depth_begin (); + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (0u, 0u, 0u)); // depth: 0 + EXPECT_EQ (it.getCurrentOctreeDepth (), 0u); + EXPECT_TRUE (it.isBranchNode ()); + ++it; + EXPECT_EQ (it, oct_a_.fixed_depth_end ()); + + // Check the iterator at depth 0 + it = oct_a_.fixed_depth_begin (0); + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (0u, 0u, 0u)); // depth: 0 + EXPECT_EQ (it.getCurrentOctreeDepth (), 0u); + EXPECT_TRUE (it.isBranchNode ()); + ++it; + EXPECT_EQ (it, oct_a_.fixed_depth_end ()); + + // Check the iterator at depth 1 + it = oct_a_.fixed_depth_begin (1); + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (0u, 0u, 0u)); // depth: 1 + EXPECT_EQ (it.getCurrentOctreeDepth (), 1u); + EXPECT_TRUE (it.isLeafNode ()); + ++it; + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (0u, 1u, 0u)); // depth: 1 + EXPECT_EQ (it.getCurrentOctreeDepth (), 1u); + EXPECT_TRUE (it.isBranchNode ()); + ++it; + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (1u, 0u, 0u)); // depth: 1 + EXPECT_EQ (it.getCurrentOctreeDepth (), 1u); + EXPECT_TRUE (it.isLeafNode ()); + ++it; + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (1u, 1u, 0u)); // depth: 1 + EXPECT_EQ (it.getCurrentOctreeDepth (), 1u); + EXPECT_TRUE (it.isBranchNode ()); + ++it; + EXPECT_EQ (it, oct_a_.fixed_depth_end ()); + + // Check the iterator at depth 2 + it = oct_a_.fixed_depth_begin (2); + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (0u, 2u, 0u)); // depth: 2 + EXPECT_EQ (it.getCurrentOctreeDepth (), 2u); + EXPECT_TRUE (it.isLeafNode ()); + ++it; + EXPECT_EQ (it.getCurrentOctreeKey (), OctreeKey (2u, 2u, 0u)); // depth: 2 + EXPECT_EQ (it.getCurrentOctreeDepth (), 2u); + EXPECT_TRUE (it.isLeafNode ()); + ++it; + EXPECT_EQ (it, oct_a_.fixed_depth_end ()); +} + +//////////////////////////////////////////////////////// +// OctreeBase Iterator Pre/Post increment +//////////////////////////////////////////////////////// + +struct OctreeBaseIteratorsPrePostTest : public OctreeBaseBeginEndIteratorsTest +{ +}; + +TEST_F (OctreeBaseIteratorsPrePostTest, DefaultIterator) +{ + // Useful types + typedef typename OctreeT::Iterator IteratorT; + + // Default initialization + IteratorT it_a_pre; + IteratorT it_a_post; + IteratorT it_a_end = oct_a_.end (); + + // Iterate over every node of the octree oct_a_. + for (it_a_pre = oct_a_.begin (), it_a_post = oct_a_.begin (); + ((it_a_pre != it_a_end) && (it_a_post != it_a_end)); ) + { + EXPECT_EQ (it_a_pre, it_a_post++); + EXPECT_EQ (++it_a_pre, it_a_post); + } + + EXPECT_EQ (it_a_pre, it_a_end); + EXPECT_EQ (it_a_post, it_a_end); +} + +TEST_F (OctreeBaseIteratorsPrePostTest, LeafNodeDepthFirstIterator) +{ + // Useful types + typedef typename OctreeT::LeafNodeDepthFirstIterator IteratorT; + + // Default initialization + IteratorT it_a_pre; + IteratorT it_a_post; + IteratorT it_a_end = oct_a_.leaf_depth_end (); + + // Iterate over every node of the octree oct_a_. + for (it_a_pre = oct_a_.leaf_depth_begin (), it_a_post = oct_a_.leaf_depth_begin (); + ((it_a_pre != it_a_end) && (it_a_post != it_a_end)); ) + { + EXPECT_EQ (it_a_pre, it_a_post++); + EXPECT_EQ (++it_a_pre, it_a_post); + } + + EXPECT_EQ (it_a_pre, it_a_end); + EXPECT_EQ (it_a_post, it_a_end); +} + +TEST_F (OctreeBaseIteratorsPrePostTest, DepthFirstIterator) +{ + // Useful types + typedef typename OctreeT::DepthFirstIterator IteratorT; + + // Default initialization + IteratorT it_a_pre; + IteratorT it_a_post; + IteratorT it_a_end = oct_a_.depth_end (); + + // Iterate over every node of the octree oct_a_. + for (it_a_pre = oct_a_.depth_begin (), it_a_post = oct_a_.depth_begin (); + ((it_a_pre != it_a_end) && (it_a_post != it_a_end)); ) + { + EXPECT_EQ (it_a_pre, it_a_post++); + EXPECT_EQ (++it_a_pre, it_a_post); + } + + EXPECT_EQ (it_a_pre, it_a_end); + EXPECT_EQ (it_a_post, it_a_end); +} + +TEST_F (OctreeBaseIteratorsPrePostTest, BreadthFirstIterator) +{ + // Useful types + typedef typename OctreeT::BreadthFirstIterator IteratorT; + + // Default initialization + IteratorT it_a_pre; + IteratorT it_a_post; + IteratorT it_a_end = oct_a_.breadth_end (); + + // Iterate over every node of the octree oct_a_. + for (it_a_pre = oct_a_.breadth_begin (), it_a_post = oct_a_.breadth_begin (); + ((it_a_pre != it_a_end) && (it_a_post != it_a_end)); ) + { + EXPECT_EQ (it_a_pre, it_a_post++); + EXPECT_EQ (++it_a_pre, it_a_post); + } + + EXPECT_EQ (it_a_pre, it_a_end); + EXPECT_EQ (it_a_post, it_a_end); +} + +TEST_F (OctreeBaseIteratorsPrePostTest, FixedDepthIterator) +{ + // Useful types + typedef typename OctreeT::FixedDepthIterator IteratorT; + + // Default initialization + IteratorT it_a_pre; + IteratorT it_a_post; + IteratorT it_a_end = oct_a_.fixed_depth_end (); + + for (unsigned int depth = 0; depth <= oct_a_.getTreeDepth (); ++depth) + { + it_a_pre = oct_a_.fixed_depth_begin (depth); + it_a_post = oct_a_.fixed_depth_begin (depth); + + + // Iterate over every node at a given depth of the octree oct_a_. + for (it_a_pre = oct_a_.fixed_depth_begin (depth), it_a_post = oct_a_.fixed_depth_begin (depth); + ((it_a_pre != it_a_end) && (it_a_post != it_a_end)); ) + { + EXPECT_EQ (it_a_pre, it_a_post++); + EXPECT_EQ (++it_a_pre, it_a_post); + } + + EXPECT_EQ (it_a_pre, it_a_end); + EXPECT_EQ (it_a_post, it_a_end); + } +} + +TEST_F (OctreeBaseIteratorsPrePostTest, LeafNodeBreadthFirstIterator) +{ + // Useful types + typedef typename OctreeT::LeafNodeBreadthFirstIterator IteratorT; + + // Default initialization + IteratorT it_a_pre; + IteratorT it_a_post; + IteratorT it_a_end = oct_a_.leaf_breadth_end (); + + // Iterate over every node of the octree oct_a_. + for (it_a_pre = oct_a_.leaf_breadth_begin (), it_a_post = oct_a_.leaf_breadth_begin (); + ((it_a_pre != it_a_end) && (it_a_post != it_a_end)); ) + { + EXPECT_EQ (it_a_pre, it_a_post++); + EXPECT_EQ (++it_a_pre, it_a_post); + } + + EXPECT_EQ (it_a_pre, it_a_end); + EXPECT_EQ (it_a_post, it_a_end); +} + +//////////////////////////////////////////////////////// +// OctreePointCloudAdjacency Begin/End Iterator Construction +//////////////////////////////////////////////////////// + +struct OctreePointCloudAdjacencyBeginEndIteratorsTest + : public testing::Test +{ + // Types + typedef pcl::PointXYZ PointT; + typedef pcl::PointCloud PointCloudT; + typedef pcl::octree::OctreePointCloudAdjacency OctreeT; + + // Methods + OctreePointCloudAdjacencyBeginEndIteratorsTest () + : oct_a_ (1) + , oct_b_ (1) + {} + + void SetUp () + { + // Replicable results + std::srand (42); + + // Generate Point Cloud + typename PointCloudT::Ptr cloud (new PointCloudT (100, 1)); + const float max_inv = 1.f / float (RAND_MAX); + for (size_t i = 0; i < 100; ++i) + { + const PointT pt (10.f * (float (std::rand ()) * max_inv - .5f), + 10.f * (float (std::rand ()) * max_inv - .5f), + 10.f * (float (std::rand ()) * max_inv - .5f)); + (*cloud)[i] = pt; + } + + // Add points to octree + oct_a_.setInputCloud (cloud); + oct_a_.addPointsFromInputCloud (); + + oct_b_.setInputCloud (cloud); + oct_b_.addPointsFromInputCloud (); + } + + // Members + OctreeT oct_a_, oct_b_; +}; + +TEST_F (OctreePointCloudAdjacencyBeginEndIteratorsTest, LeafDepthBegin) +{ + // Useful types + typedef typename OctreeT::LeafNodeDepthFirstIterator IteratorT; + + // Default initialization + IteratorT it_a_1 = oct_a_.leaf_depth_begin (); + IteratorT it_a_2 = oct_a_.leaf_depth_begin (); + IteratorT it_b = oct_b_.leaf_depth_begin (); + + EXPECT_EQ (it_a_1, it_a_2); + EXPECT_NE (it_a_1, it_b); + EXPECT_NE (it_a_2, it_b); + + // Different max depths are not the same iterators + IteratorT it_m = oct_a_.leaf_depth_begin (); + IteratorT it_m_1 = oct_a_.leaf_depth_begin (1); + IteratorT it_m_md = oct_a_.leaf_depth_begin (oct_a_.getTreeDepth ()); + IteratorT it_m_b_1 = oct_b_.leaf_depth_begin (1); + + EXPECT_NE (it_m_1, it_m_md); + EXPECT_EQ (it_m_md, it_m); // should default to tree depth + EXPECT_NE (it_m_1, it_m_b_1); +} + +TEST_F (OctreePointCloudAdjacencyBeginEndIteratorsTest, LeafDepthEnd) +{ + // Useful types + typedef typename OctreeT::LeafNodeDepthFirstIterator IteratorT; + + // Default initialization + IteratorT it_a_1 = oct_a_.leaf_depth_end (); + IteratorT it_a_2 = oct_a_.leaf_depth_end (); + IteratorT it_b = oct_b_.leaf_depth_end (); + + EXPECT_EQ (it_a_1, it_a_2); + EXPECT_NE (it_a_1, it_b); + EXPECT_NE (it_a_2, it_b); +} + +TEST_F (OctreePointCloudAdjacencyBeginEndIteratorsTest, DepthBegin) +{ + // Useful types + typedef typename OctreeT::DepthFirstIterator IteratorT; + + // Default initialization + IteratorT it_a_1 = oct_a_.depth_begin (); + IteratorT it_a_2 = oct_a_.depth_begin (); + IteratorT it_b = oct_b_.depth_begin (); + + EXPECT_EQ (it_a_1, it_a_2); + EXPECT_NE (it_a_1, it_b); + EXPECT_NE (it_a_2, it_b); + + // Different max depths are not the same iterators + IteratorT it_m = oct_a_.depth_begin (); + IteratorT it_m_1 = oct_a_.depth_begin (1); + IteratorT it_m_md = oct_a_.depth_begin (oct_a_.getTreeDepth ()); + IteratorT it_m_b_1 = oct_b_.depth_begin (1); + + EXPECT_NE (it_m_1, it_m_md); + EXPECT_EQ (it_m_md, it_m); // should default to tree depth + EXPECT_NE (it_m_1, it_m_b_1); +} + +TEST_F (OctreePointCloudAdjacencyBeginEndIteratorsTest, DepthEnd) +{ + // Useful types + typedef typename OctreeT::DepthFirstIterator IteratorT; + + // Default initialization + IteratorT it_a_1 = oct_a_.depth_end (); + IteratorT it_a_2 = oct_a_.depth_end (); + IteratorT it_b = oct_b_.depth_end (); + + EXPECT_EQ (it_a_1, it_a_2); + EXPECT_NE (it_a_1, it_b); + EXPECT_NE (it_a_2, it_b); +} + +TEST_F (OctreePointCloudAdjacencyBeginEndIteratorsTest, BreadthBegin) +{ + // Useful types + typedef typename OctreeT::BreadthFirstIterator IteratorT; + + // Default initialization + IteratorT it_a_1 = oct_a_.breadth_begin (); + IteratorT it_a_2 = oct_a_.breadth_begin (); + IteratorT it_b = oct_b_.breadth_begin (); + + EXPECT_EQ (it_a_1, it_a_2); + EXPECT_NE (it_a_1, it_b); + EXPECT_NE (it_a_2, it_b); + + // Different max depths are not the same iterators + IteratorT it_m = oct_a_.breadth_begin (); + IteratorT it_m_1 = oct_a_.breadth_begin (1); + IteratorT it_m_md = oct_a_.breadth_begin (oct_a_.getTreeDepth ()); + IteratorT it_m_b_1 = oct_b_.breadth_begin (1); + + EXPECT_NE (it_m_1, it_m_md); + EXPECT_EQ (it_m_md, it_m); // should default to tree depth + EXPECT_NE (it_m_1, it_m_b_1); +} + +TEST_F (OctreePointCloudAdjacencyBeginEndIteratorsTest, BreadthEnd) +{ + // Useful types + typedef typename OctreeT::BreadthFirstIterator IteratorT; + + // Default initialization + IteratorT it_a_1 = oct_a_.breadth_end (); + IteratorT it_a_2 = oct_a_.breadth_end (); + IteratorT it_b = oct_b_.breadth_end (); + + EXPECT_EQ (it_a_1, it_a_2); + EXPECT_NE (it_a_1, it_b); + EXPECT_NE (it_a_2, it_b); +} + +TEST_F (OctreePointCloudAdjacencyBeginEndIteratorsTest, FixedDepthBegin) +{ + // Useful types + typedef typename OctreeT::FixedDepthIterator IteratorT; + + // Default initialization + IteratorT it_a_1 = oct_a_.fixed_depth_begin (); + IteratorT it_a_2 = oct_a_.fixed_depth_begin (); + IteratorT it_b = oct_b_.fixed_depth_begin (); + + EXPECT_EQ (it_a_1, it_a_2); + EXPECT_NE (it_a_1, it_b); + EXPECT_NE (it_a_2, it_b); + + // Different max depths are not the same iterators + IteratorT it_f = oct_a_.fixed_depth_begin (); + IteratorT it_f_1 = oct_a_.fixed_depth_begin (1); + IteratorT it_f_fd = oct_a_.fixed_depth_begin (oct_a_.getTreeDepth ()); + IteratorT it_f_0 = oct_a_.fixed_depth_begin (0); + IteratorT it_f_b_1 = oct_b_.fixed_depth_begin (1); + + EXPECT_NE (it_f_1, it_f_fd); + EXPECT_NE (it_f_fd, it_f); + EXPECT_EQ (it_f_0, it_f); // should default to root tree + EXPECT_NE (it_f_1, it_f_b_1); +} + +TEST_F (OctreePointCloudAdjacencyBeginEndIteratorsTest, FixedDepthEnd) +{ + // Useful types + typedef typename OctreeT::FixedDepthIterator IteratorT; + + // Default initialization + IteratorT it_a_1 = oct_a_.fixed_depth_end (); + IteratorT it_a_2 = oct_a_.fixed_depth_end (); + IteratorT it_b = oct_b_.fixed_depth_end (); + + EXPECT_EQ (it_a_1, it_a_2); + EXPECT_NE (it_a_1, it_b); + EXPECT_NE (it_a_2, it_b); +} + +TEST_F (OctreePointCloudAdjacencyBeginEndIteratorsTest, LeafBreadthBegin) +{ + // Useful types + typedef typename OctreeT::LeafNodeBreadthFirstIterator IteratorT; + + // Default initialization + IteratorT it_a_1 = oct_a_.leaf_breadth_begin (); + IteratorT it_a_2 = oct_a_.leaf_breadth_begin (); + IteratorT it_b = oct_b_.leaf_breadth_begin (); + + EXPECT_EQ (it_a_1, it_a_2); + EXPECT_NE (it_a_1, it_b); + EXPECT_NE (it_a_2, it_b); + + // Different max depths are not the same iterators + IteratorT it_m = oct_a_.leaf_breadth_begin (); + IteratorT it_m_1 = oct_a_.leaf_breadth_begin (1); + IteratorT it_m_md = oct_a_.leaf_breadth_begin (oct_a_.getTreeDepth ()); + IteratorT it_m_b_1 = oct_b_.leaf_breadth_begin (1); + + EXPECT_NE (it_m_1, it_m_md); + EXPECT_EQ (it_m_md, it_m); // should default to tree depth + EXPECT_NE (it_m_1, it_m_b_1); +} + +TEST_F (OctreePointCloudAdjacencyBeginEndIteratorsTest, LeafBreadthEnd) +{ + // Useful types + typedef typename OctreeT::LeafNodeBreadthFirstIterator IteratorT; + + // Default initialization + IteratorT it_a_1 = oct_a_.leaf_breadth_end (); + IteratorT it_a_2 = oct_a_.leaf_breadth_end (); + IteratorT it_b = oct_b_.leaf_breadth_end (); + + EXPECT_EQ (it_a_1, it_a_2); + EXPECT_NE (it_a_1, it_b); + EXPECT_NE (it_a_2, it_b); +} + +//////////////////////////////////////////////////////// +// OctreePointCloudSierpinski Iterator Traversal Test +//////////////////////////////////////////////////////// + +struct OctreePointCloudSierpinskiTest + : public testing::Test +{ + // Types + typedef pcl::PointXYZ PointT; + typedef pcl::PointCloud PointCloudT; + typedef pcl::octree::OctreePointCloud OctreeT; + + // Methods + OctreePointCloudSierpinskiTest () + : oct_ (1) + , depth_ (7) + {} + + void SetUp () + { + // Create a point cloud which points are inside Sierpinski fractal voxel at the deepest level + // https://en.wikipedia.org/wiki/Sierpinski_triangle + typename PointCloudT::Ptr cloud (new PointCloudT); + + // The voxels will be generate between the points (0, 0, 0) and (1, 1, 1) + Eigen::Vector3f v_min (0, 0, 0); + Eigen::Vector3f v_max (1, 1, 1); + + // Generate Sierpinski fractal voxel at the deepest level + std::vector > voxels (generateSierpinskiVoxelExtremities (v_min, v_max, depth_)); + + // The number of points in each voxel + unsigned int total_nb_pt = 100000; + unsigned int nb_pt_in_voxel = total_nb_pt / pow (4, depth_); + + // Replicable results + std::srand (42); + + // Fill the point cloud + for (std::vector >::const_iterator it = voxels.begin (); + it != voxels.end (); + ++it) + { + const static float eps = std::numeric_limits::epsilon (); + double x_min = it->first.x () + eps; + double y_min = it->first.y () + eps; + double z_min = it->first.z () + eps; + double x_max = it->second.x () - eps; + double y_max = it->second.y () - eps; + double z_max = it->second.z () - eps; + + for (unsigned int i = 0; i < nb_pt_in_voxel; ++i) + { + float x = x_min + (rand () / ((float)(RAND_MAX) + 1)) * (x_max - x_min); + float y = y_min + (rand () / ((float)(RAND_MAX) + 1)) * (y_max - y_min); + float z = z_min + (rand () / ((float)(RAND_MAX) + 1)) * (z_max - z_min); + + cloud->points.push_back (PointT (x, y, z)); + } + } + + // Setup the octree + double resolution = 1.0 / (pow (2, depth_)); + oct_.setResolution (resolution); + + // Add points to octree + oct_.setInputCloud (cloud); + + // Bounding box + oct_.defineBoundingBox (0.0, 0.0, 0.0, 1.0, 1.0, 1.0); + + // Add points from cloud to octree + oct_.addPointsFromInputCloud (); + } + + // Generate a vector of Sierpinski voxels at a given 'depth_arg' + std::vector > + generateSierpinskiVoxelExtremities (const Eigen::Vector3f & v_min, const Eigen::Vector3f & v_max, + const unsigned int & depth_arg) + { + std::pair voxel (v_min, v_max); + std::vector > voxels; + voxels.push_back (voxel); + + for (unsigned int i = 0; i < depth_arg; ++i) + { + voxels = generateSierpinskiVoxelExtremitiesAux (voxels); + } + + return voxels; + } + + // Apply a recursion step to a vector of macro Sierpinski voxel + // For each voxel in the input vector 'voxels_in', 4 sub-voxels are generated + std::vector > + generateSierpinskiVoxelExtremitiesAux (const std::vector > & voxels_in) + { + std::vector > voxels_out; + + for (std::vector >::const_iterator it = voxels_in.begin (); + it != voxels_in.end (); + ++it) + { + Eigen::Vector3f v_min = it->first; + Eigen::Vector3f v_max = it->second; + Eigen::Vector3f v_mid = 0.5 * (v_min + v_max); + + std::pair voxel_0; + std::pair voxel_1; + std::pair voxel_2; + std::pair voxel_3; + + voxel_0.first = v_min; + voxel_0.second = v_mid; + voxel_1.first = Eigen::Vector3f (v_min.x (), v_mid.y (), v_mid.z ()); + voxel_1.second = Eigen::Vector3f (v_mid.x (), v_max.y (), v_max.z ()); + voxel_2.first = Eigen::Vector3f (v_mid.x (), v_min.y (), v_mid.z ()); + voxel_2.second = Eigen::Vector3f (v_max.x (), v_mid.y (), v_max.z ()); + voxel_3.first = Eigen::Vector3f (v_mid.x (), v_mid.y (), v_min.z ()); + voxel_3.second = Eigen::Vector3f (v_max.x (), v_max.y (), v_mid.z ()); + + voxels_out.push_back (voxel_0); + voxels_out.push_back (voxel_1); + voxels_out.push_back (voxel_2); + voxels_out.push_back (voxel_3); + } + + return voxels_out; + } + + /** \brief Computes the total number of parent nodes at the specified depth + * + * The octree is built such that the number of the leaf nodes is equal to + * 4^depth and the number of branch nodes is egal to (4^depth -1)/(4 - 1), + * where depth is the detph of the octree. The details of the expression + * provided for the number of branch nodes could be found at: + * https://en.wikipedia.org/wiki/Geometric_progression#Geometric_series + * \param[in] depth - The depth of the octree + * \return The total number of parent nodes at the specified depth level. + */ + static unsigned + computeTotalParentNodeCount (const unsigned depth) + { + return (pow (4, depth) - 1) / (4 - 1); + } + + // Members + OctreeT oct_; + + const unsigned depth_; +}; + +/** \brief Octree test based on a Sierpinski fractal + */ +TEST_F (OctreePointCloudSierpinskiTest, DefaultIterator) +{ + // Useful types + typedef typename OctreeT::Iterator IteratorT; + + // Check the number of branch and leaf nodes + ASSERT_EQ (oct_.getLeafCount (), pow (4, depth_)); + ASSERT_EQ (oct_.getBranchCount (), computeTotalParentNodeCount (depth_)); + + IteratorT it; + IteratorT it_end = oct_.end (); + + // Check the number of leaf and branch nodes + unsigned int node_count = 0; + unsigned int branch_count = 0; + unsigned int leaf_count = 0; + + for (it = oct_.begin (); it != it_end; ++it) + { + // store node, branch and leaf count + const pcl::octree::OctreeNode* node = it.getCurrentOctreeNode (); + if (node->getNodeType () == pcl::octree::BRANCH_NODE) + { + branch_count++; + } + else if (node->getNodeType () == pcl::octree::LEAF_NODE) + { + leaf_count++; + } + node_count++; + } + + EXPECT_EQ (leaf_count, pow (4, depth_)); + EXPECT_EQ (branch_count, computeTotalParentNodeCount (depth_)); + EXPECT_EQ (node_count, computeTotalParentNodeCount (depth_ + 1)); + + // Check the specific key/child_idx value for this octree + for (it = oct_.begin (); it != it_end; ++it) + { + for (unsigned int i = 0; i < depth_; ++i) + { + // The binary representation child_idx can only contain an even number of 1 + int child_idx = it.getCurrentOctreeKey ().getChildIdxWithDepthMask (1 << i); + EXPECT_TRUE ((child_idx == 0) || (child_idx == 3) || (child_idx == 5) || (child_idx == 6)); + } + } +} + +TEST_F (OctreePointCloudSierpinskiTest, LeafNodeDepthFirstIterator) +{ + // Useful types + typedef typename OctreeT::LeafNodeDepthFirstIterator IteratorT; + + // Check the number of branch and leaf nodes + ASSERT_EQ (oct_.getLeafCount (), pow (4, depth_)); + ASSERT_EQ (oct_.getBranchCount (), computeTotalParentNodeCount (depth_)); + + IteratorT it; + IteratorT it_end = oct_.leaf_depth_end (); + + // Check the number of leaf and branch nodes + unsigned int node_count = 0; + unsigned int branch_count = 0; + unsigned int leaf_count = 0; + + for (it = oct_.leaf_depth_begin (); it != it_end; ++it) + { + // store node, branch and leaf count + const pcl::octree::OctreeNode* node = it.getCurrentOctreeNode (); + if (node->getNodeType () == pcl::octree::BRANCH_NODE) + { + branch_count++; + } + else if (node->getNodeType () == pcl::octree::LEAF_NODE) + { + leaf_count++; + } + node_count++; + } + + EXPECT_EQ (leaf_count, pow (4, depth_)); + EXPECT_EQ (branch_count, 0); + EXPECT_EQ (node_count, pow (4, depth_)); + + // Check the specific key/child_idx value for this octree + for (it = oct_.leaf_depth_begin (); it != it_end; ++it) + { + for (unsigned int i = 0; i < depth_; ++i) + { + // The binary representation child_idx can only contain an even number of 1 + int child_idx = it.getCurrentOctreeKey ().getChildIdxWithDepthMask (1 << i); + EXPECT_TRUE ((child_idx == 0) || (child_idx == 3) || (child_idx == 5) || (child_idx == 6)); + } + } +} + +TEST_F (OctreePointCloudSierpinskiTest, DepthFirstIterator) +{ + // Useful types + typedef typename OctreeT::DepthFirstIterator IteratorT; + + // Check the number of branch and leaf nodes + ASSERT_EQ (oct_.getLeafCount (), pow (4, depth_)); + ASSERT_EQ (oct_.getBranchCount (), computeTotalParentNodeCount (depth_)); + + IteratorT it; + IteratorT it_end = oct_.depth_end (); + + // Check the number of leaf and branch nodes + unsigned int node_count = 0; + unsigned int branch_count = 0; + unsigned int leaf_count = 0; + + for (it = oct_.depth_begin (); it != it_end; ++it) + { + // store node, branch and leaf count + const pcl::octree::OctreeNode* node = it.getCurrentOctreeNode (); + if (node->getNodeType () == pcl::octree::BRANCH_NODE) + { + branch_count++; + } + else if (node->getNodeType () == pcl::octree::LEAF_NODE) + { + leaf_count++; + } + node_count++; + } + + EXPECT_EQ (leaf_count, pow (4, depth_)); + EXPECT_EQ (branch_count, computeTotalParentNodeCount (depth_)); + EXPECT_EQ (node_count, computeTotalParentNodeCount (depth_ + 1)); + + // Check the specific key/child_idx value for this octree + for (it = oct_.depth_begin (); it != it_end; ++it) + { + for (unsigned int i = 0; i < depth_; ++i) + { + // The binary representation child_idx can only contain an even number of 1 + int child_idx = it.getCurrentOctreeKey ().getChildIdxWithDepthMask (1 << i); + EXPECT_TRUE ((child_idx == 0) || (child_idx == 3) || (child_idx == 5) || (child_idx == 6)); + } + } +} + +TEST_F (OctreePointCloudSierpinskiTest, BreadthFirstIterator) +{ + // Useful types + typedef typename OctreeT::BreadthFirstIterator IteratorT; + + // Check the number of branch and leaf nodes + ASSERT_EQ (oct_.getLeafCount (), pow (4, depth_)); + ASSERT_EQ (oct_.getBranchCount (), computeTotalParentNodeCount (depth_)); + + IteratorT it; + IteratorT it_end = oct_.breadth_end (); + + // Check the number of leaf and branch nodes + unsigned int node_count = 0; + unsigned int branch_count = 0; + unsigned int leaf_count = 0; + + for (it = oct_.breadth_begin (); it != it_end; ++it) + { + // store node, branch and leaf count + const pcl::octree::OctreeNode* node = it.getCurrentOctreeNode (); + if (node->getNodeType () == pcl::octree::BRANCH_NODE) + { + branch_count++; + } + else if (node->getNodeType () == pcl::octree::LEAF_NODE) + { + leaf_count++; + } + node_count++; + } + + EXPECT_EQ (leaf_count, pow (4, depth_)); + EXPECT_EQ (branch_count, computeTotalParentNodeCount (depth_)); + EXPECT_EQ (node_count, computeTotalParentNodeCount (depth_ + 1)); + + // Check the specific key/child_idx value for this octree + for (it = oct_.breadth_begin (); it != it_end; ++it) + { + for (unsigned int i = 0; i < depth_; ++i) + { + // The binary representation child_idx can only contain an even number of 1 + int child_idx = it.getCurrentOctreeKey ().getChildIdxWithDepthMask (1 << i); + EXPECT_TRUE ((child_idx == 0) || (child_idx == 3) || (child_idx == 5) || (child_idx == 6)); + } + } +} + +TEST_F (OctreePointCloudSierpinskiTest, FixedDepthIterator) +{ + // Useful types + typedef typename OctreeT::FixedDepthIterator IteratorT; + + // Check the number of branch and leaf nodes + ASSERT_EQ (oct_.getLeafCount (), pow (4, depth_)); + ASSERT_EQ (oct_.getBranchCount (), computeTotalParentNodeCount (depth_)); + + IteratorT it; + IteratorT it_end = oct_.fixed_depth_end (); + + // Check the number of nodes at each level of the octree + unsigned int nb_nodes; + for (unsigned int idx_depth = 1; idx_depth <= depth_; ++idx_depth) + { + nb_nodes = 0; + for (it = oct_.fixed_depth_begin (idx_depth); it != it_end; ++it) + { + ASSERT_EQ (it.getCurrentOctreeDepth (), idx_depth); + ++nb_nodes; + } + + ASSERT_EQ (nb_nodes, pow (4, idx_depth)); + } + + // Check the specific key/child_idx value for this octree + for (it = oct_.fixed_depth_begin (depth_); it != it_end; ++it) + { + for (unsigned int i = 0; i < depth_; ++i) + { + // The binary representation child_idx can only contain an even number of 1 + int child_idx = it.getCurrentOctreeKey ().getChildIdxWithDepthMask (1 << i); + ASSERT_TRUE ((child_idx == 0) || (child_idx == 3) || (child_idx == 5) || (child_idx == 6)); + } + } +} + +TEST_F (OctreePointCloudSierpinskiTest, LeafNodeBreadthFirstIterator) +{ + // Useful types + typedef typename OctreeT::LeafNodeBreadthFirstIterator IteratorT; + + // Check the number of branch and leaf nodes + ASSERT_EQ (oct_.getLeafCount (), pow (4, depth_)); + ASSERT_EQ (oct_.getBranchCount (), (pow (4, depth_) - 1) / (4 - 1)); + + IteratorT it; + IteratorT it_end = oct_.leaf_breadth_end (); + + // Check the number of leaf and branch nodes + unsigned int node_count = 0; + unsigned int branch_count = 0; + unsigned int leaf_count = 0; + + for (it = oct_.leaf_breadth_begin (); it != it_end; ++it) + { + // store node, branch and leaf count + const pcl::octree::OctreeNode* node = it.getCurrentOctreeNode (); + if (node->getNodeType () == pcl::octree::BRANCH_NODE) + { + branch_count++; + } + else if (node->getNodeType () == pcl::octree::LEAF_NODE) + { + leaf_count++; + } + node_count++; + } + + EXPECT_EQ (leaf_count, pow (4, depth_)); + EXPECT_EQ (branch_count, 0); + EXPECT_EQ (node_count, pow (4, depth_)); + + // Check the specific key/child_idx value for this octree + for (it = oct_.leaf_breadth_begin (); it != it_end; ++it) + { + for (unsigned int i = 0; i < depth_; ++i) + { + // The binary representation child_idx can only contain an even number of 1 + int child_idx = it.getCurrentOctreeKey ().getChildIdxWithDepthMask (1 << i); + EXPECT_TRUE ((child_idx == 0) || (child_idx == 3) || (child_idx == 5) || (child_idx == 6)); + } + } +} + +int +main (int argc, char** const argv) +{ + testing::InitGoogleTest (&argc, argv); + return (RUN_ALL_TESTS ()); +} diff --git a/test/recognition/test_recognition_cg.cpp b/test/recognition/test_recognition_cg.cpp index d1c4f02c..1b183a18 100644 --- a/test/recognition/test_recognition_cg.cpp +++ b/test/recognition/test_recognition_cg.cpp @@ -130,12 +130,17 @@ TEST (PCL, Hough3DGrouping) clusterer.setSceneRf (scene_rf); clusterer.setModelSceneCorrespondences (model_scene_corrs_); clusterer.setHoughBinSize (0.03); - clusterer.setHoughThreshold (25); + clusterer.setHoughThreshold (10); EXPECT_TRUE (clusterer.recognize (rototranslations)); //Assertions - EXPECT_EQ (rototranslations.size (), 1); - EXPECT_LT (computeRmsE (model_, scene_, rototranslations[0]), 1E-2); + ASSERT_GE (rototranslations.size (), 1); + + // Pick transformation with lowest error + double min_rms_e = std::numeric_limits::max (); + for (size_t i = 0; i < rototranslations.size (); ++i) + min_rms_e = std::min (min_rms_e, computeRmsE (model_, scene_, rototranslations[i])); + EXPECT_LT (min_rms_e, 1E-2); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/test/registration/test_registration.cpp b/test/registration/test_registration.cpp index 33243148..44a90818 100644 --- a/test/registration/test_registration.cpp +++ b/test/registration/test_registration.cpp @@ -61,7 +61,7 @@ #include #include #include -// We need Histogram<2> to function, so we'll explicitely add kdtree_flann.hpp here +// We need Histogram<2> to function, so we'll explicitly add kdtree_flann.hpp here #include //(pcl::Histogram<2>) @@ -162,11 +162,6 @@ TEST (PCL, IterativeClosestPoint) { IterativeClosestPoint reg; PointCloud::ConstPtr source (cloud_source.makeShared ()); -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - reg.setInputCloud (source); // test for PCL_DEPRECATED - source = reg.getInputCloud (); // test for PCL_DEPRECATED -#pragma GCC diagnostic pop reg.setInputSource (source); reg.setInputTarget (cloud_target.makeShared ()); reg.setMaximumIterations (50); @@ -273,7 +268,7 @@ TEST (PCL, JointIterativeClosestPoint) reg.setMaxCorrespondenceDistance (0.25); // Making sure the correspondence distance > the max translation // Add a median distance rejector pcl::registration::CorrespondenceRejectorMedianDistance::Ptr rej_med (new pcl::registration::CorrespondenceRejectorMedianDistance); - rej_med->setMedianFactor (4.0); + rej_med->setMedianFactor (8.0); reg.addCorrespondenceRejector (rej_med); // Also add a SaC rejector pcl::registration::CorrespondenceRejectorSampleConsensus::Ptr rej_samp (new pcl::registration::CorrespondenceRejectorSampleConsensus); diff --git a/test/registration/test_registration_api.cpp b/test/registration/test_registration_api.cpp index b10acd05..362631f8 100644 --- a/test/registration/test_registration_api.cpp +++ b/test/registration/test_registration_api.cpp @@ -131,11 +131,6 @@ TEST (PCL, CorrespondenceRejectorDistance) // re-do correspondence estimation boost::shared_ptr correspondences (new pcl::Correspondences); pcl::registration::CorrespondenceEstimation corr_est; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - corr_est.setInputCloud (source); // test for PCL_DEPRECATED - source = corr_est.getInputCloud (); // test for PCL_DEPRECATED -#pragma GCC diagnostic pop corr_est.setInputSource (source); corr_est.setInputTarget (target); corr_est.determineCorrespondences (*correspondences); @@ -237,11 +232,6 @@ TEST (PCL, CorrespondenceRejectorSampleConsensus) boost::shared_ptr correspondences_result_rej_sac (new pcl::Correspondences); pcl::registration::CorrespondenceRejectorSampleConsensus corr_rej_sac; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - corr_rej_sac.setInputCloud (source); // test for PCL_DEPRECATED - source = corr_rej_sac.getInputCloud (); // test for PCL_DEPRECATED -#pragma GCC diagnostic pop corr_rej_sac.setInputSource (source); corr_rej_sac.setInputTarget (target); corr_rej_sac.setInlierThreshold (rej_sac_max_dist); @@ -536,10 +526,10 @@ TEST (PCL, TransformationEstimationLM) const Eigen::Quaternionf R_LM_1_float (T_LM_float.topLeftCorner <3, 3> ()); const Eigen::Translation3f t_LM_1_float (T_LM_float.topRightCorner <3, 1> ()); - EXPECT_NEAR (R_LM_1_float.x (), R_ref.x (), 1e-4f); - EXPECT_NEAR (R_LM_1_float.y (), R_ref.y (), 1e-4f); - EXPECT_NEAR (R_LM_1_float.z (), R_ref.z (), 1e-4f); - EXPECT_NEAR (R_LM_1_float.w (), R_ref.w (), 1e-4f); + EXPECT_NEAR (R_LM_1_float.x (), R_ref.x (), 1e-3f); + EXPECT_NEAR (R_LM_1_float.y (), R_ref.y (), 1e-3f); + EXPECT_NEAR (R_LM_1_float.z (), R_ref.z (), 1e-3f); + EXPECT_NEAR (R_LM_1_float.w (), R_ref.w (), 1e-3f); EXPECT_NEAR (t_LM_1_float.x (), t_ref.x (), 1e-3f); EXPECT_NEAR (t_LM_1_float.y (), t_ref.y (), 1e-3f); diff --git a/test/sample_consensus/test_sample_consensus.cpp b/test/sample_consensus/test_sample_consensus.cpp index 1b3833ad..997945d4 100644 --- a/test/sample_consensus/test_sample_consensus.cpp +++ b/test/sample_consensus/test_sample_consensus.cpp @@ -89,7 +89,12 @@ TEST (SampleConsensus, InfiniteLoop) cloud.points[idx].z = 0.0; } +#if defined(DEBUG) || defined(_DEBUG) + boost::posix_time::time_duration delay (0, 0, 15, 0); +#else boost::posix_time::time_duration delay (0, 0, 1, 0); +#endif + boost::function sac_function; SampleConsensusModelSpherePtr model (new SampleConsensusModelSphere (cloud.makeShared ())); diff --git a/test/sample_consensus/test_sample_consensus_line_models.cpp b/test/sample_consensus/test_sample_consensus_line_models.cpp index e3266244..6ef73dd4 100644 --- a/test/sample_consensus/test_sample_consensus_line_models.cpp +++ b/test/sample_consensus/test_sample_consensus_line_models.cpp @@ -40,6 +40,7 @@ #include +#include #include #include #include @@ -132,9 +133,11 @@ TEST (SampleConsensusModelParallelLine, RANSAC) cloud.points[15].getVector3fMap () << -1.05f, 5.01f, 5.0f; // Create a shared line model pointer directly + const double eps = 0.1; //angle eps in radians + const Eigen::Vector3f axis (0, 0, 1); SampleConsensusModelParallelLinePtr model (new SampleConsensusModelParallelLine (cloud.makeShared ())); - model->setAxis (Eigen::Vector3f (0, 0, 1)); - model->setEpsAngle (0.1); + model->setAxis (axis); + model->setEpsAngle (eps); // Create the RANSAC object RandomSampleConsensus sac (model, 0.1); @@ -154,9 +157,11 @@ TEST (SampleConsensusModelParallelLine, RANSAC) Eigen::VectorXf coeff; sac.getModelCoefficients (coeff); EXPECT_EQ (6, coeff.size ()); - EXPECT_NEAR (0, coeff[3], 1e-4); - EXPECT_NEAR (0, coeff[4], 1e-4); - EXPECT_NEAR (1, coeff[5], 1e-4); + + // Make sure the returned direction respects the angular constraint + double angle_diff = getAngle3D (axis, coeff.tail<3> ()); + angle_diff = std::min (angle_diff, M_PI - angle_diff); + EXPECT_GT (eps, angle_diff); // Projection tests PointCloud proj_points; diff --git a/test/search/test_search.cpp b/test/search/test_search.cpp index 8d997fb6..15bb02ac 100644 --- a/test/search/test_search.cpp +++ b/test/search/test_search.cpp @@ -128,7 +128,7 @@ vector unorganized_dense_cloud_query_indices; vector unorganized_sparse_cloud_query_indices; vector organized_sparse_query_indices; -/** \briet test whether the result of a search containes unique point ids or not +/** \briet test whether the result of a search contains unique point ids or not * @param indices resulting indices from a search * @param name name of the search method that returned these distances * @return true if indices are unique, false otherwise diff --git a/test/segmentation/test_non_linear.cpp b/test/segmentation/test_non_linear.cpp index 85ca0253..9f1e6285 100644 --- a/test/segmentation/test_non_linear.cpp +++ b/test/segmentation/test_non_linear.cpp @@ -73,7 +73,7 @@ TEST (SACSegmentation, Segmentation) EXPECT_NEAR (sphere_coefficients->values[2], 1.24558, 1e-2); EXPECT_NEAR (sphere_coefficients->values[3], 0.0536238, 1e-2); - EXPECT_NEAR (static_cast (inliers->indices.size ()), 3516, 10); + EXPECT_NEAR (static_cast (inliers->indices.size ()), 3516, 15); } //* ---[ */ diff --git a/test/surface/test_concave_hull.cpp b/test/surface/test_concave_hull.cpp index dbf4edeb..be6fbe90 100644 --- a/test/surface/test_concave_hull.cpp +++ b/test/surface/test_concave_hull.cpp @@ -122,10 +122,6 @@ TEST (PCL, ConcaveHull_planar_bunny) PointCloud hull_3d; concave_hull_3d.reconstruct (hull_3d); -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - EXPECT_EQ (concave_hull_3d.getDim (), 3); // test for PCL_DEPRECATED -#pragma GCC diagnostic pop EXPECT_EQ (concave_hull_3d.getDimension (), 3); ModelCoefficients::Ptr plane_coefficients (new ModelCoefficients ()); diff --git a/test/surface/test_gp3.cpp b/test/surface/test_gp3.cpp index 258a1103..032a5668 100644 --- a/test/surface/test_gp3.cpp +++ b/test/surface/test_gp3.cpp @@ -226,7 +226,7 @@ TEST (PCL, UpdateMesh_With_TextureMapping) //tex_material.tex_Ns = 0.0f; //tex_material.tex_illum = 2; - //// set texture material paramaters + //// set texture material parameters //tm.setTextureMaterials(tex_material); //// set texture files diff --git a/test/surface/test_marching_cubes.cpp b/test/surface/test_marching_cubes.cpp index 716df1ef..e4ca075a 100644 --- a/test/surface/test_marching_cubes.cpp +++ b/test/surface/test_marching_cubes.cpp @@ -76,12 +76,12 @@ TEST (PCL, MarchingCubesTest) std::vector vertices; hoppe.reconstruct (points, vertices); - EXPECT_NEAR (points.points[points.size()/2].x, -0.042528, 1e-3); - EXPECT_NEAR (points.points[points.size()/2].y, 0.080196, 1e-3); - EXPECT_NEAR (points.points[points.size()/2].z, 0.043159, 1e-3); - EXPECT_EQ (vertices[vertices.size ()/2].vertices[0], 10854); - EXPECT_EQ (vertices[vertices.size ()/2].vertices[1], 10855); - EXPECT_EQ (vertices[vertices.size ()/2].vertices[2], 10856); + EXPECT_NEAR (points.points[points.size()/2].x, -0.037143, 1e-3); + EXPECT_NEAR (points.points[points.size()/2].y, 0.098213, 1e-3); + EXPECT_NEAR (points.points[points.size()/2].z, -0.044911, 1e-3); + EXPECT_EQ (vertices[vertices.size ()/2].vertices[0], 11202); + EXPECT_EQ (vertices[vertices.size ()/2].vertices[1], 11203); + EXPECT_EQ (vertices[vertices.size ()/2].vertices[2], 11204); MarchingCubesRBF rbf; @@ -92,12 +92,12 @@ TEST (PCL, MarchingCubesTest) rbf.setOffSurfaceDisplacement (0.02f); rbf.reconstruct (points, vertices); - EXPECT_NEAR (points.points[points.size()/2].x, -0.033919, 1e-3); - EXPECT_NEAR (points.points[points.size()/2].y, 0.151683, 1e-3); - EXPECT_NEAR (points.points[points.size()/2].z, -0.000086, 1e-3); - EXPECT_EQ (vertices[vertices.size ()/2].vertices[0], 4284); - EXPECT_EQ (vertices[vertices.size ()/2].vertices[1], 4285); - EXPECT_EQ (vertices[vertices.size ()/2].vertices[2], 4286); + EXPECT_NEAR (points.points[points.size()/2].x, -0.025630, 1e-3); + EXPECT_NEAR (points.points[points.size()/2].y, 0.135228, 1e-3); + EXPECT_NEAR (points.points[points.size()/2].z, 0.035766, 1e-3); + EXPECT_EQ (vertices[vertices.size ()/2].vertices[0], 4275); + EXPECT_EQ (vertices[vertices.size ()/2].vertices[1], 4276); + EXPECT_EQ (vertices[vertices.size ()/2].vertices[2], 4277); } diff --git a/test/surface/test_moving_least_squares.cpp b/test/surface/test_moving_least_squares.cpp index d8b87414..dc396885 100644 --- a/test/surface/test_moving_least_squares.cpp +++ b/test/surface/test_moving_least_squares.cpp @@ -72,7 +72,7 @@ TEST (PCL, MovingLeastSquares) // Set parameters mls.setInputCloud (cloud); mls.setComputeNormals (true); - mls.setPolynomialFit (true); + mls.setPolynomialOrder (2); mls.setSearchMethod (tree); mls.setSearchRadius (0.03); @@ -92,7 +92,7 @@ TEST (PCL, MovingLeastSquares) MovingLeastSquaresOMP mls_omp; mls_omp.setInputCloud (cloud); mls_omp.setComputeNormals (true); - mls_omp.setPolynomialFit (true); + mls_omp.setPolynomialOrder (2); mls_omp.setSearchMethod (tree); mls_omp.setSearchRadius (0.03); mls_omp.setNumberOfThreads (4); @@ -123,7 +123,7 @@ TEST (PCL, MovingLeastSquares) // Set parameters mls_upsampling.setInputCloud (cloud); mls_upsampling.setComputeNormals (true); - mls_upsampling.setPolynomialFit (true); + mls_upsampling.setPolynomialOrder (2); mls_upsampling.setSearchMethod (tree); mls_upsampling.setSearchRadius (0.03); mls_upsampling.setUpsamplingMethod (MovingLeastSquares::SAMPLE_LOCAL_PLANE); diff --git a/test/visualization/test_visualization.cpp b/test/visualization/test_visualization.cpp index 00b43309..9f0b3ba2 100644 --- a/test/visualization/test_visualization.cpp +++ b/test/visualization/test_visualization.cpp @@ -110,8 +110,45 @@ TEST (PCL, PCLVisualizer_camera) // cerr << "reset camera viewer pose:" << endl << viewer_pose << endl; } +//////////////////////////////////////////////////////////////////////////////// +TEST (PCL, PCLVisualizer_getPointCloudRenderingProperties) +{ + PCLVisualizer visualizer; - + std::string cloud_id = "input_cloud"; + visualizer.addPointCloud (cloud, cloud_id); + ASSERT_TRUE (visualizer.setPointCloudRenderingProperties (PCL_VISUALIZER_COLOR, + 1., 0., 0., cloud_id)); + double r, g, b; + EXPECT_FALSE (visualizer.getPointCloudRenderingProperties (PCL_VISUALIZER_POINT_SIZE, + r, g, b, cloud_id)); + EXPECT_FALSE (visualizer.getPointCloudRenderingProperties (PCL_VISUALIZER_OPACITY, + r, g, b, cloud_id)); + EXPECT_FALSE (visualizer.getPointCloudRenderingProperties (PCL_VISUALIZER_LINE_WIDTH, + r, g, b, cloud_id)); + EXPECT_FALSE (visualizer.getPointCloudRenderingProperties (PCL_VISUALIZER_FONT_SIZE, + r, g, b, cloud_id)); + EXPECT_FALSE (visualizer.getPointCloudRenderingProperties (PCL_VISUALIZER_REPRESENTATION, + r, g, b, cloud_id)); + EXPECT_FALSE (visualizer.getPointCloudRenderingProperties (PCL_VISUALIZER_IMMEDIATE_RENDERING, + r, g, b, cloud_id)); + EXPECT_FALSE (visualizer.getPointCloudRenderingProperties (PCL_VISUALIZER_SHADING, + r, g, b, cloud_id)); + EXPECT_FALSE (visualizer.getPointCloudRenderingProperties (PCL_VISUALIZER_LUT, + r, g, b, cloud_id)); + EXPECT_FALSE (visualizer.getPointCloudRenderingProperties (PCL_VISUALIZER_LUT_RANGE, + r, g, b, cloud_id)); + + r = 666.; + g = 666.; + b = 666.; + EXPECT_TRUE (visualizer.getPointCloudRenderingProperties (PCL_VISUALIZER_COLOR, + r, g, b, cloud_id)); + + EXPECT_EQ (r, 1.); + EXPECT_EQ (g, 0.); + EXPECT_EQ (b, 0.); +} /* ---[ */ int diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 4095b831..50028cb4 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,11 +1,12 @@ set (SUBSYS_NAME tools) set (SUBSYS_DESC "Useful PCL-based command line tools") set (SUBSYS_DEPS common io filters sample_consensus segmentation search kdtree features surface octree registration recognition geometry keypoints ml) +set (SUBSYS_OPT_DEPS visualization) set (DEFAULT ON) set (REASON "") PCL_SUBSYS_OPTION (BUILD_tools "${SUBSYS_NAME}" "${SUBSYS_DESC}" ${DEFAULT} "${REASON}") -PCL_SUBSYS_DEPEND (BUILD_tools "${SUBSYS_NAME}" DEPS ${SUBSYS_DEPS}) +PCL_SUBSYS_DEPEND (BUILD_tools "${SUBSYS_NAME}" DEPS ${SUBSYS_DEPS} OPT_DEPS ${SUBSYS_OPT_DEPS}) if (BUILD_tools) @@ -211,8 +212,6 @@ if (BUILD_tools) if(BUILD_visualization) - PCL_SUBSYS_DEPEND(BUILD_tools "${SUBSYS_NAME}" DEPS visualization) - PCL_ADD_EXECUTABLE(pcl_obj_rec_ransac_model_opps "${SUBSYS_NAME}" obj_rec_ransac_model_opps.cpp) target_link_libraries(pcl_obj_rec_ransac_model_opps pcl_common pcl_visualization pcl_recognition) diff --git a/tools/demean_cloud.cpp b/tools/demean_cloud.cpp index c5063b08..88dd9c16 100644 --- a/tools/demean_cloud.cpp +++ b/tools/demean_cloud.cpp @@ -83,7 +83,7 @@ saveCloud (const std::string &filename, const pcl::PCLPointCloud2 &cloud) int main (int argc, char** argv) { - print_info ("Convert a PCD file to PLY format. For more information, use: %s -h\n", argv[0]); + print_info ("Convert a PCD file to a de-meaned PCD file. For more information, use: %s -h\n", argv[0]); if (argc < 3) { diff --git a/tools/extract_feature.cpp b/tools/extract_feature.cpp index f25b580a..b6e0e1b9 100644 --- a/tools/extract_feature.cpp +++ b/tools/extract_feature.cpp @@ -199,6 +199,11 @@ main (int argc, char** argv) else if (feature_name == "VFHEstimation") computeFeatureViaNormals< VFHEstimation, PointXYZ, Normal, VFHSignature308> (cloud, output, argc, argv, false); + else + { + print_error ("Valid feature names are PFHEstimation, FPFHEstimation, VFHEstimation.\n"); + return (-1); + } // Save into the second file saveCloud (argv[p_file_indices[1]], output); diff --git a/tools/lum.cpp b/tools/lum.cpp index 10ab2b81..e4041ca4 100644 --- a/tools/lum.cpp +++ b/tools/lum.cpp @@ -71,7 +71,7 @@ main (int argc, char **argv) double loopDist = 5.0; pcl::console::parse_argument (argc, argv, "-D", loopDist); - int loopCount = 20; + unsigned int loopCount = 20; pcl::console::parse_argument (argc, argv, "-c", loopCount); pcl::registration::LUM lum; diff --git a/tools/mesh2pcd.cpp b/tools/mesh2pcd.cpp index 1ab6bd48..3e739cb6 100644 --- a/tools/mesh2pcd.cpp +++ b/tools/mesh2pcd.cpp @@ -58,7 +58,7 @@ printHelp (int, char **argv) { print_error ("Syntax is: %s input.{ply,obj} output.pcd \n", argv[0]); print_info (" where options are:\n"); - print_info (" -level X = tesselated sphere level (default: "); + print_info (" -level X = tessellated sphere level (default: "); print_value ("%d", default_tesselated_sphere_level); print_info (")\n"); print_info (" -resolution X = the sphere resolution in angle increments (default: "); diff --git a/tools/mesh_sampling.cpp b/tools/mesh_sampling.cpp index 26a42d05..7065d236 100644 --- a/tools/mesh_sampling.cpp +++ b/tools/mesh_sampling.cpp @@ -58,10 +58,8 @@ uniform_deviate (int seed) inline void randomPointTriangle (float a1, float a2, float a3, float b1, float b2, float b3, float c1, float c2, float c3, - Eigen::Vector4f& p) + float r1, float r2, Eigen::Vector3f& p) { - float r1 = static_cast (uniform_deviate (rand ())); - float r2 = static_cast (uniform_deviate (rand ())); float r1sqr = std::sqrt (r1); float OneMinR1Sqr = (1 - r1sqr); float OneMinR2 = (1 - r2); @@ -77,11 +75,10 @@ randomPointTriangle (float a1, float a2, float a3, float b1, float b2, float b3, p[0] = c1; p[1] = c2; p[2] = c3; - p[3] = 0; } inline void -randPSurface (vtkPolyData * polydata, std::vector * cumulativeAreas, double totalArea, Eigen::Vector4f& p, bool calcNormal, Eigen::Vector3f& n) +randPSurface (vtkPolyData * polydata, std::vector * cumulativeAreas, double totalArea, Eigen::Vector3f& p, bool calcNormal, Eigen::Vector3f& n, bool calcColor, Eigen::Vector3f& c) { float r = static_cast (uniform_deviate (rand ()) * totalArea); @@ -103,13 +100,38 @@ randPSurface (vtkPolyData * polydata, std::vector * cumulativeAreas, dou n = v1.cross (v2); n.normalize (); } + float r1 = static_cast (uniform_deviate (rand ())); + float r2 = static_cast (uniform_deviate (rand ())); randomPointTriangle (float (A[0]), float (A[1]), float (A[2]), float (B[0]), float (B[1]), float (B[2]), - float (C[0]), float (C[1]), float (C[2]), p); + float (C[0]), float (C[1]), float (C[2]), r1, r2, p); + + if (calcColor) + { + vtkUnsignedCharArray *const colors = vtkUnsignedCharArray::SafeDownCast (polydata->GetPointData ()->GetScalars ()); + if (colors && colors->GetNumberOfComponents () == 3) + { + double cA[3], cB[3], cC[3]; + colors->GetTuple (ptIds[0], cA); + colors->GetTuple (ptIds[1], cB); + colors->GetTuple (ptIds[2], cC); + + randomPointTriangle (float (cA[0]), float (cA[1]), float (cA[2]), + float (cB[0]), float (cB[1]), float (cB[2]), + float (cC[0]), float (cC[1]), float (cC[2]), r1, r2, c); + } + else + { + static bool printed_once = false; + if (!printed_once) + PCL_WARN ("Mesh has no vertex colors, or vertex colors are not RGB!"); + printed_once = true; + } + } } void -uniform_sampling (vtkSmartPointer polydata, size_t n_samples, bool calc_normal, pcl::PointCloud & cloud_out) +uniform_sampling (vtkSmartPointer polydata, size_t n_samples, bool calc_normal, bool calc_color, pcl::PointCloud & cloud_out) { polydata->BuildCells (); vtkSmartPointer cells = polydata->GetPolys (); @@ -133,9 +155,10 @@ uniform_sampling (vtkSmartPointer polydata, size_t n_samples, bool for (i = 0; i < n_samples; i++) { - Eigen::Vector4f p; + Eigen::Vector3f p; Eigen::Vector3f n; - randPSurface (polydata, &cumulativeAreas, totalArea, p, calc_normal, n); + Eigen::Vector3f c; + randPSurface (polydata, &cumulativeAreas, totalArea, p, calc_normal, n, calc_color, c); cloud_out.points[i].x = p[0]; cloud_out.points[i].y = p[1]; cloud_out.points[i].z = p[2]; @@ -145,6 +168,12 @@ uniform_sampling (vtkSmartPointer polydata, size_t n_samples, bool cloud_out.points[i].normal_y = n[1]; cloud_out.points[i].normal_z = n[2]; } + if (calc_color) + { + cloud_out.points[i].r = static_cast(c[0]); + cloud_out.points[i].g = static_cast(c[1]); + cloud_out.points[i].b = static_cast(c[2]); + } } } @@ -168,6 +197,7 @@ printHelp (int, char **argv) print_value ("%f", default_leaf_size); print_info (" m)\n"); print_info (" -write_normals = flag to write normals to the output pcd\n"); + print_info (" -write_colors = flag to write colors to the output pcd\n"); print_info ( " -no_vis_result = flag to stop visualizing the generated pcd\n"); } @@ -192,6 +222,7 @@ main (int argc, char **argv) parse_argument (argc, argv, "-leaf_size", leaf_size); bool vis_result = ! find_switch (argc, argv, "-no_vis_result"); const bool write_normals = find_switch (argc, argv, "-write_normals"); + const bool write_colors = find_switch (argc, argv, "-write_colors"); // Parse the command line arguments for .ply and PCD files std::vector pcd_file_indices = parse_file_extension_argument (argc, argv, ".pcd"); @@ -247,44 +278,58 @@ main (int argc, char **argv) vis.spin (); } - pcl::PointCloud::Ptr cloud_1 (new pcl::PointCloud); - uniform_sampling (polydata1, SAMPLE_POINTS_, write_normals, *cloud_1); + pcl::PointCloud::Ptr cloud_1 (new pcl::PointCloud); + uniform_sampling (polydata1, SAMPLE_POINTS_, write_normals, write_colors, *cloud_1); if (INTER_VIS) { visualization::PCLVisualizer vis_sampled; - vis_sampled.addPointCloud (cloud_1); + vis_sampled.addPointCloud (cloud_1); if (write_normals) - vis_sampled.addPointCloudNormals (cloud_1, 1, 0.02f, "cloud_normals"); + vis_sampled.addPointCloudNormals (cloud_1, 1, 0.02f, "cloud_normals"); vis_sampled.spin (); } // Voxelgrid - VoxelGrid grid_; + VoxelGrid grid_; grid_.setInputCloud (cloud_1); grid_.setLeafSize (leaf_size, leaf_size, leaf_size); - pcl::PointCloud::Ptr voxel_cloud (new pcl::PointCloud); + pcl::PointCloud::Ptr voxel_cloud (new pcl::PointCloud); grid_.filter (*voxel_cloud); if (vis_result) { visualization::PCLVisualizer vis3 ("VOXELIZED SAMPLES CLOUD"); - vis3.addPointCloud (voxel_cloud); + vis3.addPointCloud (voxel_cloud); if (write_normals) - vis3.addPointCloudNormals (voxel_cloud, 1, 0.02f, "cloud_normals"); + vis3.addPointCloudNormals (voxel_cloud, 1, 0.02f, "cloud_normals"); vis3.spin (); } - if (!write_normals) + if (write_normals && write_colors) { - pcl::PointCloud::Ptr cloud_xyz (new pcl::PointCloud); + savePCDFileASCII (argv[pcd_file_indices[0]], *voxel_cloud); + } + else if (write_normals) + { + pcl::PointCloud::Ptr cloud_xyzn (new pcl::PointCloud); + // Strip uninitialized colors from cloud: + pcl::copyPointCloud (*voxel_cloud, *cloud_xyzn); + savePCDFileASCII (argv[pcd_file_indices[0]], *cloud_xyzn); + } + else if (write_colors) + { + pcl::PointCloud::Ptr cloud_xyzrgb (new pcl::PointCloud); // Strip uninitialized normals from cloud: - pcl::copyPointCloud (*voxel_cloud, *cloud_xyz); - savePCDFileASCII (argv[pcd_file_indices[0]], *cloud_xyz); + pcl::copyPointCloud (*voxel_cloud, *cloud_xyzrgb); + savePCDFileASCII (argv[pcd_file_indices[0]], *cloud_xyzrgb); } - else + else // !write_normals && !write_colors { - savePCDFileASCII (argv[pcd_file_indices[0]], *voxel_cloud); + pcl::PointCloud::Ptr cloud_xyz (new pcl::PointCloud); + // Strip uninitialized normals and colors from cloud: + pcl::copyPointCloud (*voxel_cloud, *cloud_xyz); + savePCDFileASCII (argv[pcd_file_indices[0]], *cloud_xyz); } } diff --git a/tools/mls_smoothing.cpp b/tools/mls_smoothing.cpp index 1825b945..973fcb14 100644 --- a/tools/mls_smoothing.cpp +++ b/tools/mls_smoothing.cpp @@ -49,7 +49,6 @@ using namespace pcl::io; using namespace pcl::console; int default_polynomial_order = 2; -bool default_use_polynomial_fit = false; double default_search_radius = 0.0, default_sqr_gauss_param = 0.0; @@ -63,9 +62,7 @@ printHelp (int, char **argv) print_value ("%f", default_search_radius); print_info (")\n"); print_info (" -sqr_gauss_param X = parameter used for the distance based weighting of neighbors (recommended = search_radius^2) (default: "); print_value ("%f", default_sqr_gauss_param); print_info (")\n"); - print_info (" -use_polynomial_fit X = decides whether the surface and normal are approximated using a polynomial or only via tangent estimation (default: "); - print_value ("%d", default_use_polynomial_fit); print_info (")\n"); - print_info (" -polynomial_order X = order of the polynomial to be fit (implicitly, use_polynomial_fit = 1) (default: "); + print_info (" -polynomial_order X = order of the polynomial to be fit (polynomial_order > 1, indicates using a polynomial fit) (default: "); print_value ("%d", default_polynomial_order); print_info (")\n"); } @@ -85,9 +82,12 @@ loadCloud (const std::string &filename, pcl::PCLPointCloud2 &cloud) } void -compute (const pcl::PCLPointCloud2::ConstPtr &input, pcl::PCLPointCloud2 &output, - double search_radius, bool sqr_gauss_param_set, double sqr_gauss_param, - bool use_polynomial_fit, int polynomial_order) +compute (const pcl::PCLPointCloud2::ConstPtr &input, + pcl::PCLPointCloud2 &output, + double search_radius, + bool sqr_gauss_param_set, + double sqr_gauss_param, + int polynomial_order) { PointCloud::Ptr xyz_cloud_pre (new pcl::PointCloud ()), @@ -111,7 +111,6 @@ compute (const pcl::PCLPointCloud2::ConstPtr &input, pcl::PCLPointCloud2 &output mls.setInputCloud (xyz_cloud); mls.setSearchRadius (search_radius); if (sqr_gauss_param_set) mls.setSqrGaussParam (sqr_gauss_param); - mls.setPolynomialFit (use_polynomial_fit); mls.setPolynomialOrder (polynomial_order); // mls.setUpsamplingMethod (MovingLeastSquares::SAMPLE_LOCAL_PLANE); @@ -128,8 +127,8 @@ compute (const pcl::PCLPointCloud2::ConstPtr &input, pcl::PCLPointCloud2 &output mls.setSearchMethod (tree); mls.setComputeNormals (true); - PCL_INFO ("Computing smoothed surface and normals with search_radius %f , sqr_gaussian_param %f, polynomial fitting %d, polynomial order %d\n", - mls.getSearchRadius(), mls.getSqrGaussParam(), mls.getPolynomialFit(), mls.getPolynomialOrder()); + PCL_INFO ("Computing smoothed surface and normals with search_radius %f , sqr_gaussian_param %f, polynomial order %d\n", + mls.getSearchRadius(), mls.getSqrGaussParam(), mls.getPolynomialOrder()); TicToc tt; tt.tic (); mls.process (*xyz_cloud_smoothed); @@ -178,14 +177,11 @@ main (int argc, char** argv) double sqr_gauss_param = default_sqr_gauss_param; bool sqr_gauss_param_set = true; int polynomial_order = default_polynomial_order; - bool use_polynomial_fit = default_use_polynomial_fit; parse_argument (argc, argv, "-radius", search_radius); + parse_argument (argc, argv, "-polynomial_order", polynomial_order); if (parse_argument (argc, argv, "-sqr_gauss_param", sqr_gauss_param) == -1) sqr_gauss_param_set = false; - if (parse_argument (argc, argv, "-polynomial_order", polynomial_order) != -1 ) - use_polynomial_fit = true; - parse_argument (argc, argv, "-use_polynomial_fit", use_polynomial_fit); // Load the first file pcl::PCLPointCloud2::Ptr cloud (new pcl::PCLPointCloud2); @@ -194,8 +190,7 @@ main (int argc, char** argv) // Do the smoothing pcl::PCLPointCloud2 output; - compute (cloud, output, search_radius, sqr_gauss_param_set, sqr_gauss_param, - use_polynomial_fit, polynomial_order); + compute (cloud, output, search_radius, sqr_gauss_param_set, sqr_gauss_param, polynomial_order); // Save into the second file saveCloud (argv[p_file_indices[1]], output); diff --git a/tools/octree_viewer.cpp b/tools/octree_viewer.cpp index f5ae26be..3817bfda 100644 --- a/tools/octree_viewer.cpp +++ b/tools/octree_viewer.cpp @@ -35,13 +35,14 @@ * \author Raphael Favier * */ -#include +#include #include #include #include #include #include +#include #include #include "boost.h" @@ -49,19 +50,22 @@ #include #include #include -//============================= -// Displaying cubes is very long! -// so we limit their numbers. - const int MAX_DISPLAYED_CUBES(15000); -//============================= +#include class OctreeViewer { public: OctreeViewer (std::string &filename, double resolution) : - viz ("Octree visualizator"), cloud (new pcl::PointCloud()), - displayCloud (new pcl::PointCloud()), octree (resolution), displayCubes(false), - showPointsWithCubes (false), wireframe (true) + viz ("Octree visualizator"), + cloud (new pcl::PointCloud()), + displayCloud (new pcl::PointCloud()), + cloudVoxel (new pcl::PointCloud()), + octree (resolution), + wireframe (true), + show_cubes_ (true), + show_centroids_ (false), + show_original_points_ (false), + point_size_ (1.0) { //try to load the cloud @@ -72,18 +76,24 @@ public: viz.registerKeyboardCallback(&OctreeViewer::keyboardEventOccurred, *this, 0); //key legends - viz.addText("Keys:", 0, 170, 0.0, 1.0, 0.0, "keys_t"); - viz.addText("a -> Increment displayed depth", 10, 155, 0.0, 1.0, 0.0, "key_a_t"); - viz.addText("z -> Decrement displayed depth", 10, 140, 0.0, 1.0, 0.0, "key_z_t"); - viz.addText("d -> Toggle Point/Cube representation", 10, 125, 0.0, 1.0, 0.0, "key_d_t"); - viz.addText("x -> Show/Hide original cloud", 10, 110, 0.0, 1.0, 0.0, "key_x_t"); - viz.addText("s/w -> Surface/Wireframe representation", 10, 95, 0.0, 1.0, 0.0, "key_sw_t"); + viz.addText ("Keys:", 0, 170, 0.0, 1.0, 0.0, "keys_t"); + viz.addText ("a -> Increment displayed depth", 10, 155, 0.0, 1.0, 0.0, "key_a_t"); + viz.addText ("z -> Decrement displayed depth", 10, 140, 0.0, 1.0, 0.0, "key_z_t"); + viz.addText ("v -> Toggle octree cubes representation", 10, 125, 0.0, 1.0, 0.0, "key_v_t"); + viz.addText ("b -> Toggle centroid points representation", 10, 110, 0.0, 1.0, 0.0, "key_b_t"); + viz.addText ("n -> Toggle original point cloud representation", 10, 95, 0.0, 1.0, 0.0, "key_n_t"); //set current level to half the maximum one displayedDepth = static_cast (floor (octree.getTreeDepth() / 2.0)); if (displayedDepth == 0) displayedDepth = 1; + // assign point cloud to octree + octree.setInputCloud (cloud); + + // add points from cloud to octree + octree.addPointsFromInputCloud (); + //show octree at default depth extractPointsAtLevel(displayedDepth); @@ -101,56 +111,74 @@ private: //======================================================== //visualizer pcl::PointCloud::Ptr xyz; - pcl::PointCloud::Ptr xyz_rgb; pcl::visualization::PCLVisualizer viz; //original cloud pcl::PointCloud::Ptr cloud; //displayed_cloud pcl::PointCloud::Ptr displayCloud; + // cloud which contains the voxel center + pcl::PointCloud::Ptr cloudVoxel; //octree pcl::octree::OctreePointCloudVoxelCentroid octree; //level int displayedDepth; - //bool to decide if we display points or cubes - bool displayCubes, showPointsWithCubes, wireframe; + //bool to decide what should be display + bool wireframe; + bool show_cubes_, show_centroids_, show_original_points_; + float point_size_; //======================================================== /* \brief Callback to interact with the keyboard * */ - void keyboardEventOccurred(const pcl::visualization::KeyboardEvent &event, void *) + void keyboardEventOccurred (const pcl::visualization::KeyboardEvent &event, void *) { - if (event.getKeySym() == "a" && event.keyDown()) + if (event.getKeySym () == "a" && event.keyDown ()) + { + IncrementLevel (); + } + else if (event.getKeySym () == "z" && event.keyDown ()) + { + DecrementLevel (); + } + else if (event.getKeySym () == "v" && event.keyDown ()) { - IncrementLevel(); + show_cubes_ = !show_cubes_; + update (); } - else if (event.getKeySym() == "z" && event.keyDown()) + else if (event.getKeySym () == "b" && event.keyDown ()) { - DecrementLevel(); + show_centroids_ = !show_centroids_; + update (); } - else if (event.getKeySym() == "d" && event.keyDown()) + else if (event.getKeySym () == "n" && event.keyDown ()) { - displayCubes = !displayCubes; - update(); + show_original_points_ = !show_original_points_; + update (); } - else if (event.getKeySym() == "x" && event.keyDown()) + else if (event.getKeySym () == "w" && event.keyDown ()) { - showPointsWithCubes = !showPointsWithCubes; - update(); + if (!wireframe) + wireframe = true; + update (); } - else if (event.getKeySym() == "w" && event.keyDown()) + else if (event.getKeySym () == "s" && event.keyDown ()) { - if(!wireframe) - wireframe=true; - update(); + if (wireframe) + wireframe = false; + update (); } - else if (event.getKeySym() == "s" && event.keyDown()) + else if ((event.getKeyCode () == '-') && event.keyDown ()) { - if(wireframe) - wireframe=false; - update(); + point_size_ = std::max(1.0f, point_size_ * (1 / 2.0f)); + update (); + } + else if ((event.getKeyCode () == '+') && event.keyDown ()) + { + point_size_ *= 2.0f; + update (); } } @@ -175,9 +203,8 @@ private: { std::cout << "Loading file " << filename.c_str() << std::endl; //read cloud - if (pcl::io::loadPCDFile(filename, *cloud)) + if (pcl::io::load (filename, *cloud)) { - std::cerr << "ERROR: Cannot open file " << filename << "! Aborting..." << std::endl; return false; } @@ -198,26 +225,30 @@ private: /* \brief Helper function that draw info for the user on the viewer * */ - void showLegend(bool showCubes) + void showLegend () { char dataDisplay[256]; - sprintf(dataDisplay, "Displaying data as %s", (showCubes) ? ("CUBES") : ("POINTS")); - viz.removeShape("disp_t"); - viz.addText(dataDisplay, 0, 60, 1.0, 0.0, 0.0, "disp_t"); + sprintf (dataDisplay, "Displaying octree cubes: %s", (show_cubes_) ? ("True") : ("False")); + viz.removeShape ("disp_octree_cubes"); + viz.addText (dataDisplay, 0, 75, 1.0, 0.0, 0.0, "disp_octree_cubes"); + + sprintf (dataDisplay, "Displaying centroids voxel: %s", (show_centroids_) ? ("True") : ("False")); + viz.removeShape ("disp_centroids_voxel"); + viz.addText (dataDisplay, 0, 60, 1.0, 0.0, 0.0, "disp_centroids_voxel"); + + sprintf (dataDisplay, "Displaying original point cloud: %s", (show_original_points_) ? ("True") : ("False")); + viz.removeShape ("disp_original_points"); + viz.addText (dataDisplay, 0, 45, 1.0, 0.0, 0.0, "disp_original_points"); char level[256]; - sprintf(level, "Displayed depth is %d on %d", displayedDepth, octree.getTreeDepth()); - viz.removeShape("level_t1"); - viz.addText(level, 0, 45, 1.0, 0.0, 0.0, "level_t1"); - - viz.removeShape("level_t2"); - sprintf(level, "Voxel size: %.4fm [%lu voxels]", sqrt(octree.getVoxelSquaredSideLen(displayedDepth)), - displayCloud->points.size()); - viz.addText(level, 0, 30, 1.0, 0.0, 0.0, "level_t2"); - - viz.removeShape("org_t"); - if (showPointsWithCubes) - viz.addText("Displaying original cloud", 0, 15, 1.0, 0.0, 0.0, "org_t"); + sprintf (level, "Displayed depth is %d on %d", displayedDepth, octree.getTreeDepth()); + viz.removeShape ("level_t1"); + viz.addText (level, 0, 30, 1.0, 0.0, 0.0, "level_t1"); + + viz.removeShape ("level_t2"); + sprintf (level, "Voxel size: %.4fm [%lu voxels]", std::sqrt (octree.getVoxelSquaredSideLen (displayedDepth)), + cloudVoxel->points.size ()); + viz.addText (level, 0, 15, 1.0, 0.0, 0.0, "level_t2"); } /* \brief Visual update. Create visualizations and add them to the viewer @@ -226,29 +257,30 @@ private: void update() { //remove existing shapes from visualizer - clearView(); - - //prevent the display of too many cubes - bool displayCubeLegend = displayCubes && static_cast (displayCloud->points.size ()) <= MAX_DISPLAYED_CUBES; + clearView (); - showLegend(displayCubeLegend); + showLegend (); - if (displayCubeLegend) + if (show_cubes_) { //show octree as cubes - showCubes(sqrt(octree.getVoxelSquaredSideLen(displayedDepth))); - if (showPointsWithCubes) - { - //add original cloud in visualizer - pcl::visualization::PointCloudColorHandlerGenericField color_handler(cloud, "z"); - viz.addPointCloud(cloud, color_handler, "cloud"); - } + showCubes (std::sqrt (octree.getVoxelSquaredSideLen (displayedDepth))); } - else + + if (show_centroids_) + { + //show centroid points + pcl::visualization::PointCloudColorHandlerGenericField color_handler (cloudVoxel, "x"); + viz.addPointCloud (cloudVoxel, color_handler, "cloud_centroid"); + viz.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, point_size_, "cloud_centroid"); + } + + if (show_original_points_) { - //add current cloud in visualizer - pcl::visualization::PointCloudColorHandlerGenericField color_handler(displayCloud,"z"); - viz.addPointCloud(displayCloud, color_handler, "cloud"); + //show origin point cloud + pcl::visualization::PointCloudColorHandlerGenericField color_handler (cloud, "z"); + viz.addPointCloud (cloud, color_handler, "cloud"); + viz.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, point_size_, "cloud"); } } @@ -258,69 +290,77 @@ private: void clearView() { //remove cubes if any - vtkRenderer *renderer = viz.getRenderWindow()->GetRenderers()->GetFirstRenderer(); - while (renderer->GetActors()->GetNumberOfItems() > 0) - renderer->RemoveActor(renderer->GetActors()->GetLastActor()); + vtkRenderer *renderer = viz.getRenderWindow ()->GetRenderers ()->GetFirstRenderer (); + while (renderer->GetActors ()->GetNumberOfItems () > 0) + renderer->RemoveActor (renderer->GetActors ()->GetLastActor ()); //remove point clouds if any - viz.removePointCloud("cloud"); + viz.removePointCloud ("cloud"); + viz.removePointCloud ("cloud_centroid"); } - /* \brief Create a vtkSmartPointer object containing a cube - * - */ - vtkSmartPointer GetCuboid(double minX, double maxX, double minY, double maxY, double minZ, double maxZ) - { - vtkSmartPointer cube = vtkSmartPointer::New(); - cube->SetBounds(minX, maxX, minY, maxY, minZ, maxZ); - return cube->GetOutput(); - } /* \brief display octree cubes via vtk-functions * */ void showCubes(double voxelSideLen) { - //get the renderer of the visualizer object - vtkRenderer *renderer = viz.getRenderWindow()->GetRenderers()->GetFirstRenderer(); + vtkSmartPointer appendFilter = vtkSmartPointer::New (); - vtkSmartPointer treeWireframe = vtkSmartPointer::New(); - size_t i; + // Create every cubes to be displayed double s = voxelSideLen / 2.0; - for (i = 0; i < displayCloud->points.size(); i++) + for (size_t i = 0; i < cloudVoxel->points.size (); i++) { + double x = cloudVoxel->points[i].x; + double y = cloudVoxel->points[i].y; + double z = cloudVoxel->points[i].z; + + vtkSmartPointer wk_cubeSource = vtkSmartPointer::New (); - double x = displayCloud->points[i].x; - double y = displayCloud->points[i].y; - double z = displayCloud->points[i].z; + wk_cubeSource->SetBounds (x - s, x + s, y - s, y + s, z - s, z + s); + wk_cubeSource->Update (); #if VTK_MAJOR_VERSION < 6 - treeWireframe->AddInput(GetCuboid(x - s, x + s, y - s, y + s, z - s, z + s)); + appendFilter->AddInput (wk_cubeSource->GetOutput ()); #else - treeWireframe->AddInputData (GetCuboid (x - s, x + s, y - s, y + s, z - s, z + s)); + appendFilter->AddInputData (wk_cubeSource->GetOutput ()); #endif } - vtkSmartPointer treeActor = vtkSmartPointer::New(); + // Remove any duplicate points + vtkSmartPointer cleanFilter = vtkSmartPointer::New (); - vtkSmartPointer mapper = vtkSmartPointer::New(); -#if VTK_MAJOR_VERSION < 6 - mapper->SetInput(treeWireframe->GetOutput()); -#else - mapper->SetInputData (treeWireframe->GetOutput ()); -#endif - treeActor->SetMapper(mapper); + cleanFilter->SetInputConnection (appendFilter->GetOutputPort ()); + cleanFilter->Update (); + + //Create a mapper and actor + vtkSmartPointer multiMapper = vtkSmartPointer::New (); - treeActor->GetProperty()->SetColor(1.0, 1.0, 1.0); - treeActor->GetProperty()->SetLineWidth(2); - if(wireframe) + multiMapper->SetInputConnection (cleanFilter->GetOutputPort ()); + + vtkSmartPointer multiActor = vtkSmartPointer::New (); + + multiActor->SetMapper (multiMapper); + + multiActor->GetProperty ()->SetColor (1.0, 1.0, 1.0); + multiActor->GetProperty ()->SetAmbient (1.0); + multiActor->GetProperty ()->SetLineWidth (1); + multiActor->GetProperty ()->EdgeVisibilityOn (); + multiActor->GetProperty ()->SetOpacity (1.0); + + if (wireframe) { - treeActor->GetProperty()->SetRepresentationToWireframe(); - treeActor->GetProperty()->SetOpacity(0.35); + multiActor->GetProperty ()->SetRepresentationToWireframe (); } else - treeActor->GetProperty()->SetRepresentationToSurface(); + { + multiActor->GetProperty ()->SetRepresentationToSurface (); + } + + // Add the actor to the scene + viz.getRenderWindow ()->GetRenderers ()->GetFirstRenderer ()->AddActor (multiActor); - renderer->AddActor(treeActor); + // Render and interact + viz.getRenderWindow ()->Render (); } /* \brief Extracts all the points of depth = level from the octree @@ -329,23 +369,51 @@ private: void extractPointsAtLevel(int depth) { displayCloud->points.clear(); + cloudVoxel->points.clear(); - pcl::octree::OctreePointCloudVoxelCentroid::Iterator tree_it; - pcl::octree::OctreePointCloudVoxelCentroid::Iterator tree_it_end = octree.end(); - - pcl::PointXYZ pt; + pcl::PointXYZ pt_voxel_center; + pcl::PointXYZ pt_centroid; std::cout << "===== Extracting data at depth " << depth << "... " << std::flush; double start = pcl::getTime (); - for (tree_it = octree.begin(depth); tree_it!=tree_it_end; ++tree_it) + for (pcl::octree::OctreePointCloudVoxelCentroid::FixedDepthIterator tree_it = octree.fixed_depth_begin (depth); + tree_it != octree.fixed_depth_end (); + ++tree_it) { + // Compute the point at the center of the voxel which represents the current OctreeNode Eigen::Vector3f voxel_min, voxel_max; - octree.getVoxelBounds(tree_it, voxel_min, voxel_max); + octree.getVoxelBounds (tree_it, voxel_min, voxel_max); + + pt_voxel_center.x = (voxel_min.x () + voxel_max.x ()) / 2.0f; + pt_voxel_center.y = (voxel_min.y () + voxel_max.y ()) / 2.0f; + pt_voxel_center.z = (voxel_min.z () + voxel_max.z ()) / 2.0f; + cloudVoxel->points.push_back (pt_voxel_center); + + // If the asked depth is the depth of the octree, retrieve the centroid at this LeafNode + if (octree.getTreeDepth () == (unsigned int) depth) + { + pcl::octree::OctreePointCloudVoxelCentroid::LeafNode* container = static_cast::LeafNode*> (tree_it.getCurrentOctreeNode ()); + + container->getContainer ().getCentroid (pt_centroid); + } + // Else, compute the centroid of the LeafNode under the current BranchNode + else + { + // Retrieve every centroid under the current BranchNode + pcl::octree::OctreeKey dummy_key; + pcl::PointCloud::VectorType voxelCentroids; + octree.getVoxelCentroidsRecursive (static_cast::BranchNode*> (*tree_it), dummy_key, voxelCentroids); + + // Iterate over the leafs to compute the centroid of all of them + pcl::CentroidPoint centroid; + for (size_t j = 0; j < voxelCentroids.size (); ++j) + { + centroid.add (voxelCentroids[j]); + } + centroid.get (pt_centroid); + } - pt.x = (voxel_min.x() + voxel_max.x()) / 2.0f; - pt.y = (voxel_min.y() + voxel_max.y()) / 2.0f; - pt.z = (voxel_min.z() + voxel_max.z()) / 2.0f; - displayCloud->points.push_back(pt); + displayCloud->points.push_back (pt_centroid); } double end = pcl::getTime (); diff --git a/tools/pcl_video.cpp b/tools/pcl_video.cpp index fb23741e..eb381f40 100644 --- a/tools/pcl_video.cpp +++ b/tools/pcl_video.cpp @@ -160,7 +160,7 @@ class Recorder tide::TrackEntry::Ptr track(new tide::TrackEntry(1, 1, "pointcloud2")); track->name("3D video"); track->codec_name("pcl::PCLPointCloud2"); - // Adding each level 1 element (only the first occurance, in the case of + // Adding each level 1 element (only the first occurrence, in the case of // clusters) to the index makes opening the file later much faster. segment.index.insert(std::make_pair(tracks.id(), segment.to_segment_offset(stream_.tellp()))); @@ -287,7 +287,7 @@ class Player tide::Segment segment; segment.read(stream); // The segment's date is stored as the number of nanoseconds since the - // start of the millenium. Boost::Date_Time is invaluable here. + // start of the millennium. Boost::Date_Time is invaluable here. bpt::ptime basis(boost::gregorian::date(2001, 1, 1)); bpt::time_duration sd(bpt::microseconds(segment.info.date() / 1000)); bpt::ptime seg_start(basis + sd); diff --git a/tools/png2pcd.cpp b/tools/png2pcd.cpp index 5d959599..468e4338 100644 --- a/tools/png2pcd.cpp +++ b/tools/png2pcd.cpp @@ -216,7 +216,7 @@ main (int argc, char** argv) } else { - print_error ("Unknow depth unit defined.\n"); + print_error ("Unknown depth unit defined.\n"); exit (-1); } } diff --git a/tools/uniform_sampling.cpp b/tools/uniform_sampling.cpp index fc519bcd..60caacc3 100644 --- a/tools/uniform_sampling.cpp +++ b/tools/uniform_sampling.cpp @@ -36,11 +36,15 @@ */ #include -#include +#include #include #include #include #include +#include +#include +#include +#include using namespace std; using namespace pcl; @@ -49,13 +53,12 @@ using namespace pcl::console; double default_radius = 0.01; -Eigen::Vector4f translation; -Eigen::Quaternionf orientation; - void printHelp (int, char **argv) { - print_error ("Syntax is: %s input.pcd output.pcd \n", argv[0]); + print_error ("Syntax is: %s \n", argv[0]); + print_info ("This tool rely on the file extensions to guess the good reader/writer.\n"); + print_info ("The supported extension for the point cloud are .pcd .ply and .vtk\n"); print_info (" where options are:\n"); print_info (" -radius X = use a leaf size of X,X,X to uniformly select 1 point per leaf (default: "); print_value ("%f", default_radius); print_info (")\n"); @@ -68,9 +71,12 @@ loadCloud (const string &filename, pcl::PCLPointCloud2 &cloud) print_highlight ("Loading "); print_value ("%s ", filename.c_str ()); tt.tic (); - if (loadPCDFile (filename, cloud, translation, orientation) < 0) + if (pcl::io::load (filename, cloud)) { + print_error ("Cannot found input file name (%s).\n", filename.c_str ()); return (false); - print_info ("[done, "); print_value ("%g", tt.toc ()); print_info (" ms : "); print_value ("%d", cloud.width * cloud.height); print_info (" points]\n"); + } + print_info ("[done, "); print_value ("%g", tt.toc ()); print_info (" ms : "); + print_value ("%d", cloud.width * cloud.height); print_info (" points]\n"); print_info ("Available dimensions: "); print_value ("%s\n", getFieldsList (cloud).c_str ()); return (true); @@ -96,7 +102,8 @@ compute (const pcl::PCLPointCloud2::ConstPtr &input, pcl::PCLPointCloud2 &output PointCloud output_; us.filter (output_); - print_info ("[done, "); print_value ("%g", tt.toc ()); print_info (" ms : "); print_value ("%d", output_.size()); print_info (" points]\n"); + print_info ("[done, "); print_value ("%g", tt.toc ()); print_info (" ms : "); + print_value ("%d", output_.size()); print_info (" points]\n"); // Convert data back toPCLPointCloud2 (output_, output); @@ -110,8 +117,23 @@ saveCloud (const string &filename, const pcl::PCLPointCloud2 &output) print_highlight ("Saving "); print_value ("%s ", filename.c_str ()); - PCDWriter w; - w.writeBinaryCompressed (filename, output, translation, orientation); + PCDWriter w_pcd; + PLYWriter w_ply; + std::string output_ext = boost::filesystem::extension (filename); + std::transform (output_ext.begin (), output_ext.end (), output_ext.begin (), ::tolower); + + if (output_ext.compare (".pcd") == 0) + { + w_pcd.writeBinaryCompressed (filename, output); + } + else if (output_ext.compare (".ply") == 0) + { + w_ply.writeBinary (filename, output); + } + else if (output_ext.compare (".vtk") == 0) + { + w_ply.writeBinary (filename, output); + } print_info ("[done, "); print_value ("%g", tt.toc ()); print_info (" ms : "); print_value ("%d", output.width * output.height); print_info (" points]\n"); } @@ -130,13 +152,21 @@ main (int argc, char** argv) // Parse the command line arguments for .pcd files vector p_file_indices; - p_file_indices = parse_file_extension_argument (argc, argv, ".pcd"); + vector extension; + extension.push_back (".pcd"); + extension.push_back (".ply"); + extension.push_back (".vtk"); + p_file_indices = parse_file_extension_argument (argc, argv, extension); + if (p_file_indices.size () != 2) { - print_error ("Need one input PCD file and one output PCD file to continue.\n"); + print_error ("Need one input file and one output file to continue.\n"); return (-1); } + std::string input_filename = argv[p_file_indices[0]]; + std::string output_filename = argv[p_file_indices[1]]; + // Command line parsing double radius = default_radius; parse_argument (argc, argv, "-radius", radius); @@ -145,7 +175,7 @@ main (int argc, char** argv) // Load the first file pcl::PCLPointCloud2::Ptr cloud (new pcl::PCLPointCloud2); - if (!loadCloud (argv[p_file_indices[0]], *cloud)) + if (!loadCloud (input_filename, *cloud)) return (-1); // Perform the keypoint estimation @@ -153,6 +183,5 @@ main (int argc, char** argv) compute (cloud, output, radius); // Save into the second file - saveCloud (argv[p_file_indices[1]], output); + saveCloud (output_filename, output); } - diff --git a/tools/virtual_scanner.cpp b/tools/virtual_scanner.cpp index 5dfcf797..42518038 100644 --- a/tools/virtual_scanner.cpp +++ b/tools/virtual_scanner.cpp @@ -108,7 +108,7 @@ main (int argc, char** argv) " -view_point : set the camera viewpoint from where the acquisition will take place\n" " -target_point : the target point that the camera should look at (default: 0, 0, 0)\n" " -organized <0|1> : create an organized, grid-like point cloud of width x height (1), or keep it unorganized with height = 1 (0)\n" - " -noise <0|1> : add gausian noise (1) or keep the model noiseless (0)\n" + " -noise <0|1> : add gaussian noise (1) or keep the model noiseless (0)\n" " -noise_std : use X times the standard deviation\n" ""); return (-1); diff --git a/tracking/include/pcl/tracking/impl/kld_adaptive_particle_filter_omp.hpp b/tracking/include/pcl/tracking/impl/kld_adaptive_particle_filter_omp.hpp index 0c018e4d..e1fe8630 100644 --- a/tracking/include/pcl/tracking/impl/kld_adaptive_particle_filter_omp.hpp +++ b/tracking/include/pcl/tracking/impl/kld_adaptive_particle_filter_omp.hpp @@ -3,6 +3,21 @@ #include +////////////////////////////////////////////////////////////////////////////////////////////// +template void +pcl::tracking::KLDAdaptiveParticleFilterOMPTracker::setNumberOfThreads (unsigned int nr_threads) +{ + if (nr_threads == 0) +#ifdef _OPENMP + threads_ = omp_get_num_procs(); +#else + threads_ = 1; +#endif + else + threads_ = nr_threads; +} + +////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::tracking::KLDAdaptiveParticleFilterOMPTracker::weight () { diff --git a/tracking/include/pcl/tracking/impl/particle_filter_omp.hpp b/tracking/include/pcl/tracking/impl/particle_filter_omp.hpp index 092c5d16..bfc100bc 100644 --- a/tracking/include/pcl/tracking/impl/particle_filter_omp.hpp +++ b/tracking/include/pcl/tracking/impl/particle_filter_omp.hpp @@ -3,6 +3,21 @@ #include +////////////////////////////////////////////////////////////////////////////////////////////// +template void +pcl::tracking::ParticleFilterOMPTracker::setNumberOfThreads (unsigned int nr_threads) +{ + if (nr_threads == 0) +#ifdef _OPENMP + threads_ = omp_get_num_procs(); +#else + threads_ = 1; +#endif + else + threads_ = nr_threads; +} + +////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::tracking::ParticleFilterOMPTracker::weight () { diff --git a/tracking/include/pcl/tracking/impl/pyramidal_klt.hpp b/tracking/include/pcl/tracking/impl/pyramidal_klt.hpp index 5e08b229..23cc3baa 100644 --- a/tracking/include/pcl/tracking/impl/pyramidal_klt.hpp +++ b/tracking/include/pcl/tracking/impl/pyramidal_klt.hpp @@ -512,8 +512,8 @@ pcl::tracking::PyramidalKLTTracker::track (const PointClou iprev_point[0] = floor (prev_pt[0]); iprev_point[1] = floor (prev_pt[1]); - if (iprev_point[0] < -track_width_ || iprev_point[0] >= grad_x.width || - iprev_point[1] < -track_height_ || iprev_point[1] >= grad_y.height) + if (iprev_point[0] < -track_width_ || (uint32_t) iprev_point[0] >= grad_x.width || + iprev_point[1] < -track_height_ || (uint32_t) iprev_point[1] >= grad_y.height) { if (level == 0) status [ptidx] = -1; @@ -544,13 +544,13 @@ pcl::tracking::PyramidalKLTTracker::track (const PointClou next_pt -= half_win; Eigen::Array2f prev_delta; - for (int j = 0; j < max_iterations_; j++) + for (unsigned int j = 0; j < max_iterations_; j++) { inext_pt[0] = floor (next_pt[0]); inext_pt[1] = floor (next_pt[1]); - if (inext_pt[0] < -track_width_ || inext_pt[0] >= next.width || - inext_pt[1] < -track_height_ || inext_pt[1] >= next.height) + if (inext_pt[0] < -track_width_ || (uint32_t) inext_pt[0] >= next.width || + inext_pt[1] < -track_height_ || (uint32_t) inext_pt[1] >= next.height) { if (level == 0) status[ptidx] = -1; @@ -595,8 +595,8 @@ pcl::tracking::PyramidalKLTTracker::track (const PointClou inext_point[0] = floor (next_point[0]); inext_point[1] = floor (next_point[1]); - if (inext_point[0] < -track_width_ || inext_point[0] >= next.width || - inext_point[1] < -track_height_ || inext_point[1] >= next.height) + if (inext_point[0] < -track_width_ || (uint32_t) inext_point[0] >= next.width || + inext_point[1] < -track_height_ || (uint32_t) inext_point[1] >= next.height) { status[ptidx] = -1; continue; diff --git a/tracking/include/pcl/tracking/impl/tracking.hpp b/tracking/include/pcl/tracking/impl/tracking.hpp index f714877b..18640024 100644 --- a/tracking/include/pcl/tracking/impl/tracking.hpp +++ b/tracking/include/pcl/tracking/impl/tracking.hpp @@ -58,9 +58,42 @@ namespace pcl x += static_cast (sampleNormal (mean[0], cov[0])); y += static_cast (sampleNormal (mean[1], cov[1])); z += static_cast (sampleNormal (mean[2], cov[2])); - roll += static_cast (sampleNormal (mean[3], cov[3])); - pitch += static_cast (sampleNormal (mean[4], cov[4])); - yaw += static_cast (sampleNormal (mean[5], cov[5])); + + // The roll, pitch, yaw space is not Euclidean, so if we sample roll, + // pitch, and yaw independently, we bias our sampling in a complicated + // way that depends on where in the space we are sampling. + // + // A solution is to always sample around mean = 0 and project our + // samples onto the space of rotations, SO(3). We rely on the fact + // that we are sampling with small variance, so we do not bias + // ourselves too much with this projection. We then rotate our + // samples by the user's requested mean. The benefit of this approach + // is that our distribution's properties are consistent over the space + // of rotations. + Eigen::Matrix3f current_rotation; + current_rotation = getTransformation (x, y, z, roll, pitch, yaw).rotation (); + Eigen::Quaternionf q_current_rotation (current_rotation); + + Eigen::Matrix3f mean_rotation; + mean_rotation = getTransformation (mean[0], mean[1], mean[2], + mean[3], mean[4], mean[5]).rotation (); + Eigen::Quaternionf q_mean_rotation (mean_rotation); + + // Scales 1.0 radians of variance in RPY sampling into equivalent units for quaternion sampling. + const float scale_factor = 0.2862; + + float a = sampleNormal (0, scale_factor*cov[3]); + float b = sampleNormal (0, scale_factor*cov[4]); + float c = sampleNormal (0, scale_factor*cov[5]); + + Eigen::Vector4f vec_sample_mean_0 (a, b, c, 1); + Eigen::Quaternionf q_sample_mean_0 (vec_sample_mean_0); + q_sample_mean_0.normalize (); + + Eigen::Quaternionf q_sample_user_mean = q_sample_mean_0 * q_mean_rotation * q_current_rotation; + + Eigen::Affine3f affine_R (q_sample_user_mean.toRotationMatrix ()); + pcl::getEulerAngles (affine_R, roll, pitch, yaw); } void diff --git a/tracking/include/pcl/tracking/kld_adaptive_particle_filter.h b/tracking/include/pcl/tracking/kld_adaptive_particle_filter.h index 6593dee1..009013f7 100644 --- a/tracking/include/pcl/tracking/kld_adaptive_particle_filter.h +++ b/tracking/include/pcl/tracking/kld_adaptive_particle_filter.h @@ -139,9 +139,9 @@ namespace pcl if (u == 0.) return (0.5); y = u / 2.0; - if (y < -6.) + if (y < -3.) return (0.0); - if (y > 6.) + if (y > 3.) return (1.0); if (y < 0.0) y = - y; diff --git a/tracking/include/pcl/tracking/kld_adaptive_particle_filter_omp.h b/tracking/include/pcl/tracking/kld_adaptive_particle_filter_omp.h index 64559adf..45b8e24a 100644 --- a/tracking/include/pcl/tracking/kld_adaptive_particle_filter_omp.h +++ b/tracking/include/pcl/tracking/kld_adaptive_particle_filter_omp.h @@ -46,7 +46,7 @@ namespace pcl using KLDAdaptiveParticleFilterTracker::calcBoundingBox; typedef Tracker BaseClass; - + typedef typename Tracker::PointCloudIn PointCloudIn; typedef typename PointCloudIn::Ptr PointCloudInPtr; typedef typename PointCloudIn::ConstPtr PointCloudInConstPtr; @@ -65,19 +65,20 @@ namespace pcl /** \brief Initialize the scheduler and set the number of threads to use. * \param nr_threads the number of hardware threads to use (0 sets the value back to automatic) - */ + */ KLDAdaptiveParticleFilterOMPTracker (unsigned int nr_threads = 0) : KLDAdaptiveParticleFilterTracker () - , threads_ (nr_threads) { tracker_name_ = "KLDAdaptiveParticleFilterOMPTracker"; + + setNumberOfThreads(nr_threads); } /** \brief Initialize the scheduler and set the number of threads to use. * \param nr_threads the number of hardware threads to use (0 sets the value back to automatic) */ - inline void - setNumberOfThreads (unsigned int nr_threads = 0) { threads_ = nr_threads; } + void + setNumberOfThreads (unsigned int nr_threads = 0); protected: /** \brief The number of threads the scheduler should use. */ diff --git a/tracking/include/pcl/tracking/particle_filter_omp.h b/tracking/include/pcl/tracking/particle_filter_omp.h index 208815ed..4f52c6b7 100644 --- a/tracking/include/pcl/tracking/particle_filter_omp.h +++ b/tracking/include/pcl/tracking/particle_filter_omp.h @@ -10,7 +10,7 @@ namespace pcl namespace tracking { /** \brief @b ParticleFilterOMPTracker tracks the PointCloud which is given by - setReferenceCloud within the measured PointCloud using particle filter method + setReferenceCloud within the measured PointCloud using particle filter method in parallel, using the OpenMP standard. * \author Ryohei Ueda * \ingroup tracking @@ -42,7 +42,7 @@ namespace pcl using ParticleFilterTracker::calcBoundingBox; typedef Tracker BaseClass; - + typedef typename Tracker::PointCloudIn PointCloudIn; typedef typename PointCloudIn::Ptr PointCloudInPtr; typedef typename PointCloudIn::ConstPtr PointCloudInConstPtr; @@ -61,20 +61,21 @@ namespace pcl /** \brief Initialize the scheduler and set the number of threads to use. * \param nr_threads the number of hardware threads to use (0 sets the value back to automatic) - */ + */ ParticleFilterOMPTracker (unsigned int nr_threads = 0) : ParticleFilterTracker () - , threads_ (nr_threads) { tracker_name_ = "ParticleFilterOMPTracker"; + + setNumberOfThreads(nr_threads); } /** \brief Initialize the scheduler and set the number of threads to use. * \param nr_threads the number of hardware threads to use (0 sets the value back to automatic) */ - inline void - setNumberOfThreads (unsigned int nr_threads = 0) { threads_ = nr_threads; } - + void + setNumberOfThreads (unsigned int nr_threads = 0); + protected: /** \brief The number of threads the scheduler should use. */ unsigned int threads_; diff --git a/tracking/include/pcl/tracking/pyramidal_klt.h b/tracking/include/pcl/tracking/pyramidal_klt.h index b15c60fe..7eadd10d 100644 --- a/tracking/include/pcl/tracking/pyramidal_klt.h +++ b/tracking/include/pcl/tracking/pyramidal_klt.h @@ -48,7 +48,7 @@ namespace pcl namespace tracking { /** Pyramidal Kanade Lucas Tomasi tracker. - * This is an implemntation of the Pyramidal Kanade Lucas Tomasi tracker that operates on + * This is an implementation of the Pyramidal Kanade Lucas Tomasi tracker that operates on * organized 3D keypoints with color/intensity information (this is the default behaviour but you * can alterate it by providing another operator as second template argument). It is an affine * tracker that iteratively computes the optical flow to find the best guess for a point p at t @@ -196,12 +196,12 @@ namespace pcl inline void setPointsToTrack (const pcl::PointCloud::ConstPtr& points); - /// \brief \return a pointer to the points succesfully tracked. + /// \brief \return a pointer to the points successfully tracked. inline pcl::PointCloud::ConstPtr getTrackedPoints () const { return (keypoints_); }; /** \brief \return the status of points to track. - * Status == 0 --> points succesfully tracked; + * Status == 0 --> points successfully tracked; * Status < 0 --> point is lost; * Status == -1 --> point is out of bond; * Status == -2 --> optical flow can not be computed for this point. @@ -209,7 +209,7 @@ namespace pcl inline pcl::PointIndicesConstPtr getPointsToTrackStatus () const { return (keypoints_status_); } - /** \brief Return the computed transfromation from tracked points. */ + /** \brief Return the computed transformation from tracked points. */ Eigen::Affine3f getResult () const { return (motion_); } @@ -279,7 +279,7 @@ namespace pcl * \param[out] win patch with interpolated intensity values * \param[out] grad_x_win patch with interpolated gradient along X values * \param[out] grad_y_win patch with interpolated gradient along Y values - * \param[out] covariance covariance matrix coefficents + * \param[out] covariance covariance matrix coefficients */ virtual void spatialGradient (const FloatImage& img, diff --git a/visualization/CMakeLists.txt b/visualization/CMakeLists.txt index 940fc8ef..2d9fe447 100644 --- a/visualization/CMakeLists.txt +++ b/visualization/CMakeLists.txt @@ -1,6 +1,6 @@ set(SUBSYS_NAME visualization) set(SUBSYS_DESC "Point cloud visualization library") -set(SUBSYS_DEPS common io kdtree geometry search) +set(SUBSYS_DEPS common io kdtree geometry search octree) if(NOT VTK_FOUND) set(DEFAULT FALSE) @@ -151,6 +151,8 @@ if(build) set(LIB_NAME "pcl_${SUBSYS_NAME}") PCL_ADD_LIBRARY("${LIB_NAME}" "${SUBSYS_NAME}" ${srcs} ${incs} ${common_incs} ${impl_incs} ${common_impl_incs} ${vtk_incs}) + target_include_directories("${LIB_NAME}" PUBLIC ${VTK_INCLUDE_DIRS}) + # apple workaround (continued) if(APPLE) target_link_libraries("${LIB_NAME}" "-framework Cocoa") diff --git a/visualization/include/pcl/visualization/cloud_viewer.h b/visualization/include/pcl/visualization/cloud_viewer.h index b0b8d917..c0f9edb7 100644 --- a/visualization/include/pcl/visualization/cloud_viewer.h +++ b/visualization/include/pcl/visualization/cloud_viewer.h @@ -37,6 +37,7 @@ #define PCL_CLOUD_VIEWER_H_ #include //pcl vis +#include // scoped_ptr for pre-C++11 #include @@ -199,7 +200,7 @@ namespace pcl private: /** \brief Private implementation. */ struct CloudViewer_impl; - std::auto_ptr impl_; + boost::scoped_ptr impl_; boost::signals2::connection registerMouseCallback (boost::function); diff --git a/visualization/include/pcl/visualization/common/common.h b/visualization/include/pcl/visualization/common/common.h index 0b1a8efd..6c69e953 100644 --- a/visualization/include/pcl/visualization/common/common.h +++ b/visualization/include/pcl/visualization/common/common.h @@ -131,7 +131,8 @@ namespace pcl PCL_VISUALIZER_LUT_HSV_INVERSE, /**< Inverse HSV colormap */ PCL_VISUALIZER_LUT_GREY, /**< Grey colormap (black to white) */ PCL_VISUALIZER_LUT_BLUE2RED, /**< Blue to red colormap (blue to white to red) */ - PCL_VISUALIZER_LUT_RANGE_AUTO /**< Set LUT range to min and max values of the data */ + PCL_VISUALIZER_LUT_RANGE_AUTO, /**< Set LUT range to min and max values of the data */ + PCL_VISUALIZER_LUT_VIRIDIS /**< Viridis colormap */ }; /** \brief Generate a lookup table for a colormap. diff --git a/visualization/include/pcl/visualization/impl/pcl_visualizer.hpp b/visualization/include/pcl/visualization/impl/pcl_visualizer.hpp index 0953c283..263f15b8 100644 --- a/visualization/include/pcl/visualization/impl/pcl_visualizer.hpp +++ b/visualization/include/pcl/visualization/impl/pcl_visualizer.hpp @@ -245,10 +245,11 @@ pcl::visualization::PCLVisualizer::convertPointCloudToVTKPolyData ( float *data = (static_cast (points->GetData ()))->GetPointer (0); // Set the points + vtkIdType ptr = 0; if (cloud->is_dense) { - for (vtkIdType i = 0; i < nr_points; ++i) - memcpy (&data[i * 3], &cloud->points[i].x, 12); // sizeof (float) * 3 + for (vtkIdType i = 0; i < nr_points; ++i, ptr += 3) + std::copy (&cloud->points[i].x, &cloud->points[i].x + 3, &data[ptr]); } else { @@ -261,7 +262,7 @@ pcl::visualization::PCLVisualizer::convertPointCloudToVTKPolyData ( !pcl_isfinite (cloud->points[i].z)) continue; - memcpy (&data[j * 3], &cloud->points[i].x, 12); // sizeof (float) * 3 + std::copy (&cloud->points[i].x, &cloud->points[i].x + 3, &data[ptr]); j++; } nr_points = j; @@ -600,7 +601,9 @@ pcl::visualization::PCLVisualizer::addSphere (const PointT ¢er, double radiu actor->GetProperty ()->SetRepresentationToSurface (); actor->GetProperty ()->SetInterpolationToFlat (); actor->GetProperty ()->SetColor (r, g, b); +#if VTK_RENDERING_BACKEND_OPENGL_VERSION < 2 actor->GetMapper ()->ImmediateModeRenderingOn (); +#endif actor->GetMapper ()->StaticOn (); actor->GetMapper ()->ScalarVisibilityOff (); actor->GetMapper ()->Update (); @@ -669,10 +672,34 @@ pcl::visualization::PCLVisualizer::addText3D ( else tid = id; - if (contains (tid)) + if (viewport < 0) + return false; + + // If there is no custom viewport and the viewport number is not 0, exit + if (rens_->GetNumberOfItems () <= viewport) { - PCL_WARN ("[addText3D] The id <%s> already exists! Please choose a different id and retry.\n", id.c_str ()); - return (false); + PCL_ERROR ("[addText3D] The viewport [%d] doesn't exist (id <%s>)! ", + viewport, + tid.c_str ()); + return false; + } + + // check all or an individual viewport for a similar id + rens_->InitTraversal (); + for (size_t i = viewport; rens_->GetNextItem () != NULL; ++i) + { + const std::string uid = tid + std::string (i, '*'); + if (contains (uid)) + { + PCL_ERROR ( "[addText3D] The id <%s> already exists in viewport [%d]! " + "Please choose a different id and retry.\n", + tid.c_str (), + i); + return false; + } + + if (viewport > 0) + break; } vtkSmartPointer textSource = vtkSmartPointer::New (); @@ -684,7 +711,7 @@ pcl::visualization::PCLVisualizer::addText3D ( // Since each follower may follow a different camera, we need different followers rens_->InitTraversal (); - vtkRenderer* renderer = NULL; + vtkRenderer* renderer; int i = 0; while ((renderer = rens_->GetNextItem ()) != NULL) { @@ -703,10 +730,8 @@ pcl::visualization::PCLVisualizer::addText3D ( // Save the pointer/ID pair to the global actor map. If we are saving multiple vtkFollowers // for multiple viewport - std::string alternate_tid = tid; - alternate_tid.append(i, '*'); - - (*shape_actor_map_)[(viewport == 0) ? tid : alternate_tid] = textActor; + const std::string uid = tid + std::string (i, '*'); + (*shape_actor_map_)[uid] = textActor; } ++i; @@ -715,6 +740,87 @@ pcl::visualization::PCLVisualizer::addText3D ( return (true); } +////////////////////////////////////////////////// +template bool +pcl::visualization::PCLVisualizer::addText3D ( + const std::string &text, + const PointT& position, + double orientation[3], + double textScale, + double r, + double g, + double b, + const std::string &id, + int viewport) +{ + std::string tid; + if (id.empty ()) + tid = text; + else + tid = id; + + if (viewport < 0) + return false; + + // If there is no custom viewport and the viewport number is not 0, exit + if (rens_->GetNumberOfItems () <= viewport) + { + PCL_ERROR ("[addText3D] The viewport [%d] doesn't exist (id <%s>)! ", + viewport, + tid.c_str ()); + return false; + } + + // check all or an individual viewport for a similar id + rens_->InitTraversal (); + for (size_t i = viewport; rens_->GetNextItem () != NULL; ++i) + { + const std::string uid = tid + std::string (i, '*'); + if (contains (uid)) + { + PCL_ERROR ( "[addText3D] The id <%s> already exists in viewport [%d]! " + "Please choose a different id and retry.\n", + tid.c_str (), + i); + return false; + } + + if (viewport > 0) + break; + } + + vtkSmartPointer textSource = vtkSmartPointer::New (); + textSource->SetText (text.c_str()); + textSource->Update (); + + vtkSmartPointer textMapper = vtkSmartPointer::New (); + textMapper->SetInputConnection (textSource->GetOutputPort ()); + + vtkSmartPointer textActor = vtkSmartPointer::New (); + textActor->SetMapper (textMapper); + textActor->SetPosition (position.x, position.y, position.z); + textActor->SetScale (textScale); + textActor->GetProperty ()->SetColor (r, g, b); + textActor->SetOrientation (orientation); + + // Save the pointer/ID pair to the global actor map. If we are saving multiple vtkFollowers + rens_->InitTraversal (); + int i = 0; + for ( vtkRenderer* renderer = rens_->GetNextItem (); + renderer != NULL; + renderer = rens_->GetNextItem (), ++i) + { + if (viewport == 0 || viewport == i) + { + renderer->AddActor (textActor); + const std::string uid = tid + std::string (i, '*'); + (*shape_actor_map_)[uid] = textActor; + } + } + + return (true); +} + ////////////////////////////////////////////////////////////////////////////////////////////// template bool pcl::visualization::PCLVisualizer::addPointCloudNormals ( @@ -737,6 +843,13 @@ pcl::visualization::PCLVisualizer::addPointCloudNormals ( PCL_ERROR ("[addPointCloudNormals] The number of points differs from the number of normals!\n"); return (false); } + + if (normals->empty ()) + { + PCL_WARN ("[addPointCloudNormals] An empty normal cloud is given! Nothing to display.\n"); + return (false); + } + if (contains (id)) { PCL_WARN ("[addPointCloudNormals] The id <%s> already exists! Please choose a different id and retry.\n", id.c_str ()); @@ -1428,7 +1541,9 @@ pcl::visualization::PCLVisualizer::updatePointCloud (const typename pcl::PointCl double minmax[2]; minmax[0] = std::numeric_limits::min (); minmax[1] = std::numeric_limits::max (); +#if VTK_RENDERING_BACKEND_OPENGL_VERSION < 2 am_it->second.actor->GetMapper ()->ImmediateModeRenderingOff (); +#endif am_it->second.actor->GetMapper ()->SetScalarRange (minmax); // Update the mapper @@ -1464,7 +1579,9 @@ pcl::visualization::PCLVisualizer::updatePointCloud (const typename pcl::PointCl double minmax[2]; minmax[0] = std::numeric_limits::min (); minmax[1] = std::numeric_limits::max (); +#if VTK_RENDERING_BACKEND_OPENGL_VERSION < 2 am_it->second.actor->GetMapper ()->ImmediateModeRenderingOff (); +#endif am_it->second.actor->GetMapper ()->SetScalarRange (minmax); // Update the mapper @@ -1502,12 +1619,12 @@ pcl::visualization::PCLVisualizer::updatePointCloud (const typename pcl::PointCl // Get a pointer to the beginning of the data array float *data = (static_cast (points->GetData ()))->GetPointer (0); - int pts = 0; + vtkIdType pts = 0; // If the dataset is dense (no NaNs) if (cloud->is_dense) { for (vtkIdType i = 0; i < nr_points; ++i, pts += 3) - memcpy (&data[pts], &cloud->points[i].x, 12); // sizeof (float) * 3 + std::copy (&cloud->points[i].x, &cloud->points[i].x + 3, &data[pts]); } else { @@ -1517,8 +1634,7 @@ pcl::visualization::PCLVisualizer::updatePointCloud (const typename pcl::PointCl // Check if the point is invalid if (!isFinite (cloud->points[i])) continue; - - memcpy (&data[pts], &cloud->points[i].x, 12); // sizeof (float) * 3 + std::copy (&cloud->points[i].x, &cloud->points[i].x + 3, &data[pts]); pts += 3; j++; } @@ -1533,15 +1649,23 @@ pcl::visualization::PCLVisualizer::updatePointCloud (const typename pcl::PointCl vertices->SetCells (nr_points, cells); // Get the colors from the handler - vtkSmartPointer scalars; - color_handler.getColor (scalars); + bool has_colors = false; double minmax[2]; - scalars->GetRange (minmax); - // Update the data - polydata->GetPointData ()->SetScalars (scalars); + vtkSmartPointer scalars; + if (color_handler.getColor (scalars)) + { + // Update the data + polydata->GetPointData ()->SetScalars (scalars); + scalars->GetRange (minmax); + has_colors = true; + } +#if VTK_RENDERING_BACKEND_OPENGL_VERSION < 2 am_it->second.actor->GetMapper ()->ImmediateModeRenderingOff (); - am_it->second.actor->GetMapper ()->SetScalarRange (minmax); +#endif + + if (has_colors) + am_it->second.actor->GetMapper ()->SetScalarRange (minmax); // Update the mapper #if VTK_MAJOR_VERSION < 6 @@ -1580,17 +1704,16 @@ pcl::visualization::PCLVisualizer::addPolygonMesh ( colors = vtkSmartPointer::New (); colors->SetNumberOfComponents (3); colors->SetName ("Colors"); - - pcl::RGB rgb_data; + uint32_t offset = fields[rgb_idx].offset; for (size_t i = 0; i < cloud->size (); ++i) { if (!isFinite (cloud->points[i])) continue; - memcpy (&rgb_data, reinterpret_cast (&cloud->points[i]) + fields[rgb_idx].offset, sizeof (pcl::RGB)); + const pcl::RGB* const rgb_data = reinterpret_cast(reinterpret_cast (&cloud->points[i]) + offset); unsigned char color[3]; - color[0] = rgb_data.r; - color[1] = rgb_data.g; - color[2] = rgb_data.b; + color[0] = rgb_data->r; + color[1] = rgb_data->g; + color[2] = rgb_data->b; colors->InsertNextTupleValue (color); } } @@ -1604,13 +1727,13 @@ pcl::visualization::PCLVisualizer::addPolygonMesh ( // Get a pointer to the beginning of the data array float *data = static_cast (points->GetData ())->GetPointer (0); - int ptr = 0; + vtkIdType ptr = 0; std::vector lookup; // If the dataset is dense (no NaNs) if (cloud->is_dense) { for (vtkIdType i = 0; i < nr_points; ++i, ptr += 3) - memcpy (&data[ptr], &cloud->points[i].x, sizeof (float) * 3); + std::copy (&cloud->points[i].x, &cloud->points[i].x + 3, &data[ptr]); } else { @@ -1623,7 +1746,7 @@ pcl::visualization::PCLVisualizer::addPolygonMesh ( continue; lookup[i] = static_cast (j); - memcpy (&data[ptr], &cloud->points[i].x, sizeof (float) * 3); + std::copy (&cloud->points[i].x, &cloud->points[i].x + 3, &data[ptr]); j++; ptr += 3; } @@ -1763,7 +1886,7 @@ pcl::visualization::PCLVisualizer::updatePolygonMesh ( if (cloud->is_dense) { for (vtkIdType i = 0; i < nr_points; ++i, ptr += 3) - memcpy (&data[ptr], &cloud->points[i].x, sizeof (float) * 3); + std::copy (&cloud->points[i].x, &cloud->points[i].x + 3, &data[ptr]); } else { @@ -1776,7 +1899,7 @@ pcl::visualization::PCLVisualizer::updatePolygonMesh ( continue; lookup [i] = static_cast (j); - memcpy (&data[ptr], &cloud->points[i].x, sizeof (float) * 3); + std::copy (&cloud->points[i].x, &cloud->points[i].x + 3, &data[ptr]); j++; ptr += 3; } @@ -1795,19 +1918,17 @@ pcl::visualization::PCLVisualizer::updatePolygonMesh ( rgb_idx = pcl::getFieldIndex (*cloud, "rgba", fields); if (rgb_idx != -1 && colors) { - pcl::RGB rgb_data; int j = 0; + uint32_t offset = fields[rgb_idx].offset; for (size_t i = 0; i < cloud->size (); ++i) { if (!isFinite (cloud->points[i])) continue; - memcpy (&rgb_data, - reinterpret_cast (&cloud->points[i]) + fields[rgb_idx].offset, - sizeof (pcl::RGB)); + const pcl::RGB* const rgb_data = reinterpret_cast(reinterpret_cast (&cloud->points[i]) + offset); unsigned char color[3]; - color[0] = rgb_data.r; - color[1] = rgb_data.g; - color[2] = rgb_data.b; + color[0] = rgb_data->r; + color[1] = rgb_data->g; + color[2] = rgb_data->b; colors->SetTupleValue (j++, color); } } diff --git a/visualization/include/pcl/visualization/interactor_style.h b/visualization/include/pcl/visualization/interactor_style.h index 6b64fc5b..f3b3d24b 100644 --- a/visualization/include/pcl/visualization/interactor_style.h +++ b/visualization/include/pcl/visualization/interactor_style.h @@ -107,9 +107,9 @@ namespace pcl */ class PCL_EXPORTS PCLVisualizerInteractorStyle : public vtkInteractorStyleRubberBandPick { - typedef boost::shared_ptr CloudActorMapPtr; - public: + typedef boost::shared_ptr CloudActorMapPtr; + static PCLVisualizerInteractorStyle *New (); /** \brief Empty constructor. */ @@ -355,7 +355,7 @@ namespace pcl /** \brief Get camera parameters from a string vector. * \param[in] camera A string vector: * Clipping Range, Focal Point, Position, ViewUp, Distance, Field of View Y, Window Size, Window Pos. - * Values in each string are seperated by a ',' + * Values in each string are separated by a ',' */ bool getCameraParameters (const std::vector &camera); diff --git a/visualization/include/pcl/visualization/pcl_plotter.h b/visualization/include/pcl/visualization/pcl_plotter.h index 09acbd26..6b5f1e79 100644 --- a/visualization/include/pcl/visualization/pcl_plotter.h +++ b/visualization/include/pcl/visualization/pcl_plotter.h @@ -188,7 +188,7 @@ namespace pcl std::vector const &color = std::vector()); /** \brief Adds a plot based on a space/tab delimited table provided in a file - * \param[in] filename name of the file containing the table. 1st column represents the values of X-Axis. Rest of the columns represent the corresponding values in Y-Axes. First row of the file is concidered for naming/labling of the plot. The plot-names should not contain any space in between. + * \param[in] filename name of the file containing the table. 1st column represents the values of X-Axis. Rest of the columns represent the corresponding values in Y-Axes. First row of the file is considered for naming/labeling of the plot. The plot-names should not contain any space in between. * \param[in] type type of the graph plotted. vtkChart::LINE for line plot, vtkChart::BAR for bar plot, and vtkChart::POINTS for a scattered point plot */ void diff --git a/visualization/include/pcl/visualization/pcl_visualizer.h b/visualization/include/pcl/visualization/pcl_visualizer.h index 30067ee2..3ae9a8d6 100644 --- a/visualization/include/pcl/visualization/pcl_visualizer.h +++ b/visualization/include/pcl/visualization/pcl_visualizer.h @@ -103,7 +103,8 @@ namespace pcl */ PCLVisualizer (const std::string &name = "", const bool create_interactor = true); - /** \brief PCL Visualizer constructor. + /** \brief PCL Visualizer constructor. It looks through the passed argv arguments to find the "-cam *.cam" argument. + * If the search failed, the name for cam file is calculated with boost uuid. If there is no such file, camera is not initilalized. * \param[in] argc * \param[in] argv * \param[in] name the window name (empty by default) @@ -112,6 +113,26 @@ namespace pcl */ PCLVisualizer (int &argc, char **argv, const std::string &name = "", PCLVisualizerInteractorStyle* style = PCLVisualizerInteractorStyle::New (), const bool create_interactor = true); + + /** \brief PCL Visualizer constructor. + * \param[in] custom vtk renderer + * \param[in] custom vtk render window + * \param[in] create_interactor if true (default), create an interactor, false otherwise + */ + PCLVisualizer (vtkSmartPointer ren, vtkSmartPointer wind, const std::string &name = "", const bool create_interactor = true); + + /** \brief PCL Visualizer constructor. + * \param[in] argc + * \param[in] argv + * \param[in] custom vtk renderer + * \param[in] custom vtk render window + * \param[in] style interactor style (defaults to PCLVisualizerInteractorStyle) + * \param[in] create_interactor if true (default), create an interactor, false otherwise + */ + PCLVisualizer (int &argc, char **argv, vtkSmartPointer ren, vtkSmartPointer wind, const std::string &name = "", + PCLVisualizerInteractorStyle* style = PCLVisualizerInteractorStyle::New (), + const bool create_interactor = true); + /** \brief PCL Visualizer destructor. */ virtual ~PCLVisualizer (); @@ -273,16 +294,6 @@ namespace pcl /** \brief Disables the Orientatation Marker Widget so it is removed from the renderer */ void removeOrientationMarkerWidgetAxes (); - - /** \brief Adds 3D axes describing a coordinate system to screen at 0,0,0. - * \param[in] scale the scale of the axes (default: 1) - * \param[in] viewport the view port where the 3D axes should be added (default: all) - */ - PCL_DEPRECATED ( - "addCoordinateSystem (scale, viewport) is deprecated, please use function " - "addCoordinateSystem (scale, id, viewport) with id a unique string identifier.") - void - addCoordinateSystem (double scale, int viewport); /** \brief Adds 3D axes describing a coordinate system to screen at 0,0,0. * \param[in] scale the scale of the axes (default: 1) @@ -292,19 +303,6 @@ namespace pcl void addCoordinateSystem (double scale = 1.0, const std::string& id = "reference", int viewport = 0); - /** \brief Adds 3D axes describing a coordinate system to screen at x, y, z - * \param[in] scale the scale of the axes (default: 1) - * \param[in] x the X position of the axes - * \param[in] y the Y position of the axes - * \param[in] z the Z position of the axes - * \param[in] viewport the view port where the 3D axes should be added (default: all) - */ - PCL_DEPRECATED ( - "addCoordinateSystem (scale, x, y, z, viewport) is deprecated, please use function " - "addCoordinateSystem (scale, x, y, z, id, viewport) with id a unique string identifier.") - void - addCoordinateSystem (double scale, float x, float y, float z, int viewport); - /** \brief Adds 3D axes describing a coordinate system to screen at x, y, z * \param[in] scale the scale of the axes (default: 1) * \param[in] x the X position of the axes @@ -316,18 +314,6 @@ namespace pcl void addCoordinateSystem (double scale, float x, float y, float z, const std::string &id = "reference", int viewport = 0); - /** \brief Adds 3D axes describing a coordinate system to screen at x, y, z, Roll,Pitch,Yaw - * - * \param[in] scale the scale of the axes (default: 1) - * \param[in] t transformation matrix - * \param[in] viewport the view port where the 3D axes should be added (default: all) - */ - PCL_DEPRECATED ( - "addCoordinateSystem (scale, t, viewport) is deprecated, please use function " - "addCoordinateSystem (scale, t, id, viewport) with id a unique string identifier.") - void - addCoordinateSystem (double scale, const Eigen::Affine3f& t, int viewport); - /** \brief Adds 3D axes describing a coordinate system to screen at x, y, z, Roll,Pitch,Yaw * * \param[in] scale the scale of the axes (default: 1) @@ -366,15 +352,6 @@ namespace pcl void addCoordinateSystem (double scale, const Eigen::Affine3f& t, const std::string &id = "reference", int viewport = 0); - /** \brief Removes a previously added 3D axes (coordinate system) - * \param[in] viewport view port where the 3D axes should be removed from (default: all) - */ - PCL_DEPRECATED ( - "removeCoordinateSystem (viewport) is deprecated, please use function " - "addCoordinateSystem (id, viewport) with id a unique string identifier.") - bool - removeCoordinateSystem (int viewport); - /** \brief Removes a previously added 3D axes (coordinate system) * \param[in] id the coordinate system object id (default: reference) * \param[in] viewport view port where the 3D axes should be removed from (default: all) @@ -579,6 +556,28 @@ namespace pcl double r = 1.0, double g = 1.0, double b = 1.0, const std::string &id = "", int viewport = 0); + /** \brief Add a 3d text to the scene + * \param[in] text the text to add + * \param[in] position the world position where the text should be added + * \param[in] orientation the angles of rotation of the text around X, Y and Z axis, + in this order. The way the rotations are effectively done is the + Z-X-Y intrinsic rotations: + https://en.wikipedia.org/wiki/Euler_angles#Definition_by_intrinsic_rotations + * \param[in] textScale the scale of the text to render + * \param[in] r the red color value + * \param[in] g the green color value + * \param[in] b the blue color value + * \param[in] id the text object id (default: equal to the "text" parameter) + * \param[in] viewport the view port (default: all) + */ + template bool + addText3D (const std::string &text, + const PointT &position, + double orientation[3], + double textScale = 1.0, + double r = 1.0, double g = 1.0, double b = 1.0, + const std::string &id = "", int viewport = 0); + /** \brief Check if the cloud, shape, or coordinate with the given id was already added to this visualizer. * \param[in] id the id of the cloud, shape, or coordinate to check * \return true if a cloud, shape, or coordinate with the specified id was found @@ -1181,6 +1180,19 @@ namespace pcl getPointCloudRenderingProperties (int property, double &value, const std::string &id = "cloud"); + /** \brief Get the rendering properties of a PointCloud + * \param[in] property the property type + * \param[out] val1 the resultant property value + * \param[out] val2 the resultant property value + * \param[out] val3 the resultant property value + * \param[in] id the point cloud object id (default: cloud) + * \return True if the property is effectively retrieved. + * \note The list of properties can be found in \ref pcl::visualization::LookUpTableRepresentationProperties. + */ + bool + getPointCloudRenderingProperties (RenderingProperties property, double &val1, double &val2, double &val3, + const std::string &id = "cloud"); + /** \brief Set whether the point cloud is selected or not * \param[in] selected whether the cloud is selected or not (true = selected) * \param[in] id the point cloud object id (default: cloud) @@ -1200,7 +1212,19 @@ namespace pcl setShapeRenderingProperties (int property, double value, const std::string &id, int viewport = 0); - /** \brief Set the rendering properties of a shape (3x values - e.g., RGB) + /** \brief Set the rendering properties of a shape (2x values - e.g., LUT minmax values) + * \param[in] property the property type + * \param[in] val1 the first value to be set + * \param[in] val2 the second value to be set + * \param[in] id the shape object id + * \param[in] viewport the view port where the shape's properties should be modified (default: all) + * \note When using \ref addPolygonMesh you you should use \ref setPointCloudRenderingProperties + */ + bool + setShapeRenderingProperties (int property, double val1, double val2, + const std::string &id, int viewport = 0); + + /** \brief Set the rendering properties of a shape (3x values - e.g., RGB) * \param[in] property the property type * \param[in] val1 the first value to be set * \param[in] val2 the second value to be set @@ -1527,6 +1551,37 @@ namespace pcl const std::string &id = "line", int viewport = 0); + /** \brief Add a line from a set of given model coefficients + * \param[in] coefficients the model coefficients (point_on_line, direction) + * \param[in] id the line id/name (default: "line") + * \param[in] viewport (optional) the id of the new viewport (default: 0) + * + * \code + * // The following are given (or computed using sample consensus techniques) + * // See SampleConsensusModelLine for more information + * // Eigen::Vector3f point_on_line, line_direction; + * + * pcl::ModelCoefficients line_coeff; + * line_coeff.values.resize (6); // We need 6 values + * line_coeff.values[0] = point_on_line.x (); + * line_coeff.values[1] = point_on_line.y (); + * line_coeff.values[2] = point_on_line.z (); + * + * line_coeff.values[3] = line_direction.x (); + * line_coeff.values[4] = line_direction.y (); + * line_coeff.values[5] = line_direction.z (); + * + * addLine (line_coeff); + * \endcode + */ + bool + addLine (const pcl::ModelCoefficients &coefficients, + const char *id = "line", + int viewport = 0) + { + return addLine (coefficients, std::string (id), viewport); + } + /** \brief Add a plane from a set of given model coefficients * \param[in] coefficients the model coefficients (a, b, c, d with ax+by+cz+d=0) * \param[in] id the plane id/name (default: "plane") @@ -1650,6 +1705,11 @@ namespace pcl void setShowFPS (bool show_fps); + /** Get the current rendering framerate. + * \see setShowFPS */ + float + getFPS () const; + /** \brief Renders a virtual scene as seen from the camera viewpoint and returns the rendered point cloud. * ATT: This method will only render the scene if only on viewport exists. Otherwise, returns an empty * point cloud and exits immediately. @@ -1922,7 +1982,36 @@ namespace pcl vtkSmartPointer interactor_; #endif private: - struct ExitMainLoopTimerCallback : public vtkCommand + /** \brief Internal function for renderer setup + * \param[in] vtk renderer + */ + void setupRenderer (vtkSmartPointer ren); + + /** \brief Internal function for setting up FPS callback + * \param[in] vtk renderer + */ + void setupFPSCallback (const vtkSmartPointer& ren); + + /** \brief Internal function for setting up render window + * \param[in] name the window name + */ + void setupRenderWindow (const std::string& name); + + /** \brief Internal function for setting up interactor style + */ + void setupStyle (); + + /** \brief Internal function for setting the default render window size and position on screen + */ + void setDefaultWindowSizeAndPos (); + + /** \brief Internal function for setting up camera parameters + * \param[in] argc + * \param[in] argv + */ + void setupCamera (int &argc, char **argv); + + struct PCL_EXPORTS ExitMainLoopTimerCallback : public vtkCommand { static ExitMainLoopTimerCallback* New () { @@ -1935,7 +2024,7 @@ namespace pcl PCLVisualizer* pcl_visualizer; }; - struct ExitCallback : public vtkCommand + struct PCL_EXPORTS ExitCallback : public vtkCommand { static ExitCallback* New () { @@ -1948,20 +2037,21 @@ namespace pcl }; ////////////////////////////////////////////////////////////////////////////////////////////// - struct FPSCallback : public vtkCommand + struct PCL_EXPORTS FPSCallback : public vtkCommand { static FPSCallback *New () { return (new FPSCallback); } - FPSCallback () : actor (), pcl_visualizer (), decimated () {} - FPSCallback (const FPSCallback& src) : vtkCommand (), actor (src.actor), pcl_visualizer (src.pcl_visualizer), decimated (src.decimated) {} - FPSCallback& operator = (const FPSCallback& src) { actor = src.actor; pcl_visualizer = src.pcl_visualizer; decimated = src.decimated; return (*this); } + FPSCallback () : actor (), pcl_visualizer (), decimated (), last_fps(0.0f) {} + FPSCallback (const FPSCallback& src) : vtkCommand (), actor (src.actor), pcl_visualizer (src.pcl_visualizer), decimated (src.decimated), last_fps (src.last_fps) {} + FPSCallback& operator = (const FPSCallback& src) { actor = src.actor; pcl_visualizer = src.pcl_visualizer; decimated = src.decimated; last_fps = src.last_fps; return (*this); } virtual void Execute (vtkObject*, unsigned long event_id, void*); - + vtkTextActor *actor; PCLVisualizer* pcl_visualizer; bool decimated; + float last_fps; }; /** \brief The FPSCallback object for the current visualizer. */ @@ -2221,7 +2311,7 @@ namespace pcl vtkTexture* vtk_tex) const; /** \brief Get camera file for camera parameter saving/restoring from command line. - * Camera filename is calculated using sha1 value of all pathes of input .pcd files + * Camera filename is calculated using sha1 value of all paths of input .pcd files * \return empty string if failed. */ std::string diff --git a/visualization/include/pcl/visualization/point_cloud_color_handlers.h b/visualization/include/pcl/visualization/point_cloud_color_handlers.h index 74b4ebeb..2ff26385 100644 --- a/visualization/include/pcl/visualization/point_cloud_color_handlers.h +++ b/visualization/include/pcl/visualization/point_cloud_color_handlers.h @@ -488,6 +488,7 @@ namespace pcl virtual std::string getName () const { return ("PointCloudColorHandlerRGBAField"); } + private: // Members derived from the base class using PointCloudColorHandler::cloud_; using PointCloudColorHandler::capable_; diff --git a/visualization/include/pcl/visualization/point_cloud_geometry_handlers.h b/visualization/include/pcl/visualization/point_cloud_geometry_handlers.h index c4a4de4b..9c6fecd2 100644 --- a/visualization/include/pcl/visualization/point_cloud_geometry_handlers.h +++ b/visualization/include/pcl/visualization/point_cloud_geometry_handlers.h @@ -179,7 +179,7 @@ namespace pcl ////////////////////////////////////////////////////////////////////////////////////// /** \brief Surface normal handler class for PointCloud geometry. Given an input * dataset, all data present in fields "normal_x", "normal_y", and "normal_z" is - * extracted and dislayed on screen as XYZ data. + * extracted and displayed on screen as XYZ data. * \author Radu B. Rusu * \ingroup visualization */ @@ -244,6 +244,7 @@ namespace pcl const std::string &x_field_name, const std::string &y_field_name, const std::string &z_field_name) + : pcl::visualization::PointCloudGeometryHandler::PointCloudGeometryHandler (cloud) { field_x_idx_ = pcl::getFieldIndex (*cloud, x_field_name, fields_); if (field_x_idx_ == -1) @@ -427,7 +428,7 @@ namespace pcl ////////////////////////////////////////////////////////////////////////////////////// /** \brief Surface normal handler class for PointCloud geometry. Given an input * dataset, all data present in fields "normal_x", "normal_y", and "normal_z" is - * extracted and dislayed on screen as XYZ data. + * extracted and displayed on screen as XYZ data. * \author Radu B. Rusu * \ingroup visualization */ diff --git a/visualization/include/pcl/visualization/point_picking_event.h b/visualization/include/pcl/visualization/point_picking_event.h index 6c03df97..7506bc84 100644 --- a/visualization/include/pcl/visualization/point_picking_event.h +++ b/visualization/include/pcl/visualization/point_picking_event.h @@ -118,7 +118,7 @@ namespace pcl /** \brief For situations when multiple points are selected in a sequence, return the point coordinates. * \param[out] x1 the x coordinate of the first point that got selected by the user * \param[out] y1 the y coordinate of the first point that got selected by the user - * \param[out] z1 the z coordinate of the firts point that got selected by the user + * \param[out] z1 the z coordinate of the first point that got selected by the user * \param[out] x2 the x coordinate of the second point that got selected by the user * \param[out] y2 the y coordinate of the second point that got selected by the user * \param[out] z2 the z coordinate of the second point that got selected by the user diff --git a/visualization/include/pcl/visualization/simple_buffer_visualizer.h b/visualization/include/pcl/visualization/simple_buffer_visualizer.h index 75521c7c..d340a50d 100644 --- a/visualization/include/pcl/visualization/simple_buffer_visualizer.h +++ b/visualization/include/pcl/visualization/simple_buffer_visualizer.h @@ -63,7 +63,7 @@ namespace pcl // load values into cloud updateValuesToDisplay(); - // check if we need to automatically handle the backgroud color + // check if we need to automatically handle the background color if(control_background_color_) { if(values_.back() < lowest_threshold_) @@ -155,7 +155,7 @@ namespace pcl } private: - /** \brief initialize ther buffer that stores the values to zero. + /** \brief initialize the buffer that stores the values to zero. * \note The size is set by private member nb_values_ which is in the range [2-308]. */ void @@ -210,13 +210,13 @@ namespace pcl */ int nb_values_; - /** \brief boolean used to know if we need to change the backgroud color in case of low values. */ + /** \brief boolean used to know if we need to change the background color in case of low values. */ bool control_background_color_; /** \brief threshold to turn the background orange if latest value is lower. */ float lowest_threshold_; - /** \brief boolean used to know if we need to change the backgroud color in case of low values. True means we do it ourselves. */ + /** \brief boolean used to know if we need to change the background color in case of low values. True means we do it ourselves. */ bool handle_y_scale_; /** \brief float tracking the minimal and maximal values ever observed. */ diff --git a/visualization/include/pcl/visualization/vtk/vtkRenderWindowInteractorFix.mm b/visualization/include/pcl/visualization/vtk/vtkRenderWindowInteractorFix.mm index 10304393..de8b1615 100644 --- a/visualization/include/pcl/visualization/vtk/vtkRenderWindowInteractorFix.mm +++ b/visualization/include/pcl/visualization/vtk/vtkRenderWindowInteractorFix.mm @@ -144,7 +144,7 @@ { [self breakEventLoop]; - // The NSWindow is closing, so prevent anyone from accidently using it + // The NSWindow is closing, so prevent anyone from accidentally using it renWin->SetRootWindow(NULL); } } diff --git a/visualization/include/pcl/visualization/vtk/vtkVertexBufferObject.h b/visualization/include/pcl/visualization/vtk/vtkVertexBufferObject.h index a124e0a1..a8266f82 100644 --- a/visualization/include/pcl/visualization/vtk/vtkVertexBufferObject.h +++ b/visualization/include/pcl/visualization/vtk/vtkVertexBufferObject.h @@ -84,7 +84,7 @@ public: // - StreamRead specified once by R, queried a few times by A // - StreamCopy specified once by R, used a few times S // - StaticDraw specified once by A, used many times S - // - StaticRead specificed once by R, queried many times by A + // - StaticRead specified once by R, queried many times by A // - StaticCopy specified once by R, used many times S // - DynamicDraw respecified repeatedly by A, used many times S // - DynamicRead respecified repeatedly by R, queried many times by A diff --git a/visualization/src/common/common.cpp b/visualization/src/common/common.cpp index 556ebd0c..0ef6bd29 100644 --- a/visualization/src/common/common.cpp +++ b/visualization/src/common/common.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include ////////////////////////////////////////////////////////////////////////////////////////////// @@ -434,7 +435,23 @@ pcl::visualization::getColormapLUT (LookUpTableRepresentationProperties colormap red[2] * weight + white[2] * (1 - weight) ); } break; - } + } + + case PCL_VISUALIZER_LUT_VIRIDIS: + { + table->SetSaturationRange (1, 1); + table->SetAlphaRange (1, 1); + table->SetNumberOfTableValues (pcl::ViridisLUT::size ()); + for (size_t i = 0; i < pcl::ViridisLUT::size (); i++) + { + pcl::RGB c = pcl::ViridisLUT::at (i); + table->SetTableValue (i, static_cast (c.r) / 255.0, + static_cast (c.g) / 255.0, + static_cast (c.b) / 255.0); + } + break; + } + default: PCL_WARN ("[pcl::visualization::getColormapLUT] Requested colormap type does not exist!\n"); return false; diff --git a/visualization/src/interactor_style.cpp b/visualization/src/interactor_style.cpp index 94d6923f..2839ba50 100644 --- a/visualization/src/interactor_style.cpp +++ b/visualization/src/interactor_style.cpp @@ -236,7 +236,7 @@ pcl::visualization::PCLVisualizerInteractorStyle::setCameraParameters (const Eig window_size[0] = 2 * static_cast (intrinsics (0, 2)); window_size[1] = 2 * static_cast (intrinsics (1, 2)); - // Compute the vertical field of view based on the focal length and image heigh + // Compute the vertical field of view based on the focal length and image height double fovy = 2 * atan (window_size[1] / (2. * intrinsics (1, 1))) * 180.0 / M_PI; diff --git a/visualization/src/pcl_painter2D.cpp b/visualization/src/pcl_painter2D.cpp index 4d47290d..64ae7757 100644 --- a/visualization/src/pcl_painter2D.cpp +++ b/visualization/src/pcl_painter2D.cpp @@ -364,7 +364,7 @@ pcl::visualization::PCLPainter2D::Paint (vtkContext2D *painter) //draw every figures for (size_t i = 0; i < figures_.size (); i++) { - figures_[i]->draw (painter); //thats it ;-) + figures_[i]->draw (painter); //that's it ;-) } return true; diff --git a/visualization/src/pcl_plotter.cpp b/visualization/src/pcl_plotter.cpp index 2c5f8c29..711a8f9a 100644 --- a/visualization/src/pcl_plotter.cpp +++ b/visualization/src/pcl_plotter.cpp @@ -616,7 +616,7 @@ pcl::visualization::PCLPlotter::computeHistogram ( if (pcl_isfinite (data[i])) { unsigned int index = (unsigned int) (floor ((data[i] - min) / size)); - if (index == nbins) index = nbins - 1; //including right boundary + if (index == (unsigned int) nbins) index = nbins - 1; //including right boundary histogram[index].second++; } } diff --git a/visualization/src/pcl_visualizer.cpp b/visualization/src/pcl_visualizer.cpp index 5b123311..afdddb35 100644 --- a/visualization/src/pcl_visualizer.cpp +++ b/visualization/src/pcl_visualizer.cpp @@ -96,11 +96,19 @@ #include #include +#if VTK_MAJOR_VERSION > 7 +#include +#endif + #include #include #include #include +#if (BOOST_VERSION >= 106600) +#include +#else #include +#endif #include #include @@ -128,7 +136,7 @@ pcl::visualization::PCLVisualizer::PCLVisualizer (const std::string &name, const , exit_main_loop_timer_callback_ () , exit_callback_ () , rens_ (vtkSmartPointer::New ()) - , win_ () + , win_ (vtkSmartPointer::New ()) , style_ (vtkSmartPointer::New ()) , cloud_actor_map_ (new CloudActorMap) , shape_actor_map_ (new ShapeActorMap) @@ -136,54 +144,15 @@ pcl::visualization::PCLVisualizer::PCLVisualizer (const std::string &name, const , camera_set_ () , camera_file_loaded_ (false) { - // Create a Renderer vtkSmartPointer ren = vtkSmartPointer::New (); - ren->AddObserver (vtkCommand::EndEvent, update_fps_); - // Add it to the list of renderers - rens_->AddItem (ren); - - // FPS callback - vtkSmartPointer txt = vtkSmartPointer::New (); - update_fps_->actor = txt; - update_fps_->pcl_visualizer = this; - update_fps_->decimated = false; - ren->AddActor (txt); - txt->SetInput("0 FPS"); - - // Create a RendererWindow - win_ = vtkSmartPointer::New (); - win_->SetWindowName (name.c_str ()); - - // Get screen size - int scr_size_x = win_->GetScreenSize ()[0]; - int scr_size_y = win_->GetScreenSize ()[1]; - // Set the window size as 1/2 of the screen size - win_->SetSize (scr_size_x / 2, scr_size_y / 2); - - // By default, don't use vertex buffer objects - use_vbos_ = false; - - // Add all renderers to the window - rens_->InitTraversal (); - vtkRenderer* renderer = NULL; - while ((renderer = rens_->GetNextItem ()) != NULL) - win_->AddRenderer (renderer); - - // Set renderer window in case no interactor is created - style_->setRenderWindow (win_); - - // Create the interactor style - style_->Initialize (); - style_->setRendererCollection (rens_); - style_->setCloudActorMap (cloud_actor_map_); - style_->setShapeActorMap (shape_actor_map_); - style_->UseTimersOn (); - style_->setUseVbos(use_vbos_); + setupRenderer (ren); + setupFPSCallback (ren); + setupRenderWindow (name); + setDefaultWindowSizeAndPos (); + setupStyle (); if (create_interactor) createInteractor (); - - win_->SetWindowName (name.c_str ()); } ///////////////////////////////////////////////////////////////////////////////////////////// @@ -197,7 +166,7 @@ pcl::visualization::PCLVisualizer::PCLVisualizer (int &argc, char **argv, const , exit_main_loop_timer_callback_ () , exit_callback_ () , rens_ (vtkSmartPointer::New ()) - , win_ () + , win_ (vtkSmartPointer::New ()) , style_ (style) , cloud_actor_map_ (new CloudActorMap) , shape_actor_map_ (new ShapeActorMap) @@ -205,78 +174,84 @@ pcl::visualization::PCLVisualizer::PCLVisualizer (int &argc, char **argv, const , camera_set_ () , camera_file_loaded_ (false) { - // Create a Renderer vtkSmartPointer ren = vtkSmartPointer::New (); - ren->AddObserver (vtkCommand::EndEvent, update_fps_); - // Add it to the list of renderers - rens_->AddItem (ren); + setupRenderer (ren); + setupFPSCallback (ren); + setupRenderWindow (name); + setupStyle (); + setupCamera (argc, argv); - // FPS callback - vtkSmartPointer txt = vtkSmartPointer::New (); - update_fps_->actor = txt; - update_fps_->pcl_visualizer = this; - update_fps_->decimated = false; - ren->AddActor (txt); - txt->SetInput("0 FPS"); + if(!camera_set_ && !camera_file_loaded_) + setDefaultWindowSizeAndPos (); - // Create a RendererWindow - win_ = vtkSmartPointer::New (); - win_->SetWindowName (name.c_str ()); - - // By default, don't use vertex buffer objects - use_vbos_ = false; - - // Add all renderers to the window - rens_->InitTraversal (); - vtkRenderer* renderer = NULL; - while ((renderer = rens_->GetNextItem ()) != NULL) - win_->AddRenderer (renderer); - - // Set renderer window in case no interactor is created - style_->setRenderWindow (win_); + if (create_interactor) + createInteractor (); - // Create the interactor style - style_->Initialize (); - style_->setRendererCollection (rens_); - style_->setCloudActorMap (cloud_actor_map_); - style_->setShapeActorMap (shape_actor_map_); - style_->UseTimersOn (); + //window name should be reset due to its reset somewhere in camera initialization + win_->SetWindowName (name.c_str ()); +} - // Get screen size - int scr_size_x = win_->GetScreenSize ()[0]; - int scr_size_y = win_->GetScreenSize ()[1]; +///////////////////////////////////////////////////////////////////////////////////////////// +pcl::visualization::PCLVisualizer::PCLVisualizer (vtkSmartPointer ren, vtkSmartPointer wind, + const std::string &name, const bool create_interactor) + : interactor_ () + , update_fps_ (vtkSmartPointer::New ()) +#if !((VTK_MAJOR_VERSION == 5) && (VTK_MINOR_VERSION <= 4)) + , stopped_ () + , timer_id_ () +#endif + , exit_main_loop_timer_callback_ () + , exit_callback_ () + , rens_ (vtkSmartPointer::New ()) + , win_ (wind) + , style_ (vtkSmartPointer::New ()) + , cloud_actor_map_ (new CloudActorMap) + , shape_actor_map_ (new ShapeActorMap) + , coordinate_actor_map_ (new CoordinateActorMap) + , camera_set_ () + , camera_file_loaded_ (false) +{ + setupRenderer (ren); + setupFPSCallback (ren); + setupRenderWindow (name); + setDefaultWindowSizeAndPos (); + setupStyle (); - // Set default camera parameters - initCameraParameters (); + if (create_interactor) + createInteractor (); +} - // Parse the camera settings and update the internal camera - camera_set_ = getCameraParameters (argc, argv); - // Calculate unique camera filename for camera parameter saving/restoring - if (!camera_set_) - { - std::string camera_file = getUniqueCameraFile (argc, argv); - if (!camera_file.empty ()) - { - if (boost::filesystem::exists (camera_file) && style_->loadCameraParameters (camera_file)) - { - camera_file_loaded_ = true; - } - else - { - style_->setCameraFile (camera_file); - } - } - } - // Set the window size as 1/2 of the screen size or the user given parameter +///////////////////////////////////////////////////////////////////////////////////////////// +pcl::visualization::PCLVisualizer::PCLVisualizer (int &argc, char **argv, vtkSmartPointer ren, vtkSmartPointer wind, + const std::string &name, PCLVisualizerInteractorStyle* style, const bool create_interactor) + : interactor_ () + , update_fps_ (vtkSmartPointer::New ()) +#if !((VTK_MAJOR_VERSION == 5) && (VTK_MINOR_VERSION <= 4)) + , stopped_ () + , timer_id_ () +#endif + , exit_main_loop_timer_callback_ () + , exit_callback_ () + , rens_ (vtkSmartPointer::New ()) + , win_ (wind) + , style_ (style) + , cloud_actor_map_ (new CloudActorMap) + , shape_actor_map_ (new ShapeActorMap) + , coordinate_actor_map_ (new CoordinateActorMap) + , camera_set_ () + , camera_file_loaded_ (false) +{ + setupRenderer (ren); + setupFPSCallback (ren); + setupRenderWindow (name); + setupStyle (); + setupCamera (argc, argv); if (!camera_set_ && !camera_file_loaded_) - { - win_->SetSize (scr_size_x/2, scr_size_y/2); - win_->SetPosition (0, 0); - } - + setDefaultWindowSizeAndPos (); if (create_interactor) createInteractor (); + //window name should be reset due to its reset somewhere in camera initialization win_->SetWindowName (name.c_str ()); } @@ -387,6 +362,103 @@ pcl::visualization::PCLVisualizer::setupInteractor ( // iren->SetPicker (pp); } +///////////////////////////////////////////////////////////////////////////////////////////// +void pcl::visualization::PCLVisualizer::setupRenderer (vtkSmartPointer ren) +{ + if (!ren) + PCL_ERROR ("Passed pointer to renderer is null"); + + ren->AddObserver (vtkCommand::EndEvent, update_fps_); + // Add it to the list of renderers + rens_->AddItem (ren); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void pcl::visualization::PCLVisualizer::setupFPSCallback (const vtkSmartPointer& ren) +{ + if (!ren) + PCL_ERROR ("Passed pointer to renderer is null"); + // FPS callback + vtkSmartPointer txt = vtkSmartPointer::New (); + update_fps_->actor = txt; + update_fps_->pcl_visualizer = this; + update_fps_->decimated = false; + ren->AddActor (txt); + txt->SetInput ("0 FPS"); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void pcl::visualization::PCLVisualizer::setupRenderWindow (const std::string& name) +{ + if (!win_) + PCL_ERROR ("Pointer to render window is null"); + + win_->SetWindowName (name.c_str ()); + + // By default, don't use vertex buffer objects + use_vbos_ = false; + + // Add all renderers to the window + rens_->InitTraversal (); + vtkRenderer* renderer = NULL; + while ((renderer = rens_->GetNextItem ()) != NULL) + win_->AddRenderer (renderer); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void pcl::visualization::PCLVisualizer::setupStyle () +{ + if (!style_) + PCL_ERROR ("Pointer to style is null"); + + // Set rend erer window in case no interactor is created + style_->setRenderWindow (win_); + + // Create the interactor style + style_->Initialize (); + style_->setRendererCollection (rens_); + style_->setCloudActorMap (cloud_actor_map_); + style_->setShapeActorMap (shape_actor_map_); + style_->UseTimersOn (); + style_->setUseVbos (use_vbos_); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void pcl::visualization::PCLVisualizer::setDefaultWindowSizeAndPos () +{ + if (!win_) + PCL_ERROR ("Pointer to render window is null"); + int scr_size_x = win_->GetScreenSize ()[0]; + int scr_size_y = win_->GetScreenSize ()[1]; + win_->SetSize (scr_size_x / 2, scr_size_y / 2); + win_->SetPosition (0, 0); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void pcl::visualization::PCLVisualizer::setupCamera (int &argc, char **argv) +{ + initCameraParameters (); + + // Parse the camera settings and update the internal camera + camera_set_ = getCameraParameters (argc, argv); + // Calculate unique camera filename for camera parameter saving/restoring + if (!camera_set_) + { + std::string camera_file = getUniqueCameraFile (argc, argv); + if (!camera_file.empty ()) + { + if (boost::filesystem::exists (camera_file) && style_->loadCameraParameters (camera_file)) + { + camera_file_loaded_ = true; + } + else + { + style_->setCameraFile (camera_file); + } + } + } +} + ///////////////////////////////////////////////////////////////////////////////////////////// pcl::visualization::PCLVisualizer::~PCLVisualizer () { @@ -470,7 +542,8 @@ pcl::visualization::PCLVisualizer::spin () resetStoppedFlag (); // Render the window before we start the interactor win_->Render (); - interactor_->Start (); + if (interactor_) + interactor_->Start (); } ///////////////////////////////////////////////////////////////////////////////////////////// @@ -487,6 +560,9 @@ pcl::visualization::PCLVisualizer::spinOnce (int time, bool force_redraw) } #endif + if (!interactor_) + return; + if (time <= 0) time = 1; @@ -541,31 +617,6 @@ pcl::visualization::PCLVisualizer::removeOrientationMarkerWidgetAxes () } } -///////////////////////////////////////////////////////////////////////////////////////////// -void -pcl::visualization::PCLVisualizer::addCoordinateSystem (double scale, int viewport) -{ - addCoordinateSystem (scale, "reference", viewport); -} - -void -pcl::visualization::PCLVisualizer::addCoordinateSystem (double scale, float x, float y, float z, int viewport) -{ - addCoordinateSystem (scale, x, y, z, "reference", viewport); -} - -void -pcl::visualization::PCLVisualizer::addCoordinateSystem (double scale, const Eigen::Affine3f& t, int viewport) -{ - addCoordinateSystem (scale, t, "reference", viewport); -} - -bool -pcl::visualization::PCLVisualizer::removeCoordinateSystem (int viewport) -{ - return (removeCoordinateSystem ("reference", viewport)); -} - ///////////////////////////////////////////////////////////////////////////////////////////// void pcl::visualization::PCLVisualizer::addCoordinateSystem (double scale, const std::string &id, int viewport) @@ -851,23 +902,51 @@ pcl::visualization::PCLVisualizer::removeShape (const std::string &id, int viewp bool pcl::visualization::PCLVisualizer::removeText3D (const std::string &id, int viewport) { - // Check to see if the given ID entry exists - ShapeActorMap::iterator am_it = shape_actor_map_->find (id); + if (viewport < 0) + return false; - if (am_it == shape_actor_map_->end ()) + bool success = true; + + // If there is no custom viewport and the viewport number is not 0, exit + if (rens_->GetNumberOfItems () <= viewport) { - //pcl::console::print_warn (stderr, "[removeSape] Could not find any shape with id <%s>!\n", id.c_str ()); - return (false); + PCL_ERROR ("[removeText3D] The viewport [%d] doesn't exist (id <%s>)! ", + viewport, + id.c_str ()); + return false; } - // Remove it from all renderers - if (removeActorFromRenderer (am_it->second, viewport)) + // check all or an individual viewport for a similar id + rens_->InitTraversal (); + for (size_t i = viewport; rens_->GetNextItem () != NULL; ++i) { - // Remove the pointer/ID pair to the global actor map - shape_actor_map_->erase (am_it); - return (true); + const std::string uid = id + std::string (i, '*'); + ShapeActorMap::iterator am_it = shape_actor_map_->find (uid); + + // was it found + if (am_it == shape_actor_map_->end ()) + { + if (viewport > 0) + return (false); + + continue; + } + + // Remove it from all renderers + if (removeActorFromRenderer (am_it->second, i)) + { + // Remove the pointer/ID pair to the global actor map + shape_actor_map_->erase (am_it); + if (viewport > 0) + return (true); + + success &= true; + } + else + success = false; } - return (false); + + return success; } ///////////////////////////////////////////////////////////////////////////////////////////// @@ -1146,7 +1225,9 @@ pcl::visualization::PCLVisualizer::createActorFromVTKDataSet (const vtkSmartPoin mapper->ScalarVisibilityOn (); } } +#if VTK_RENDERING_BACKEND_OPENGL_VERSION < 2 mapper->ImmediateModeRenderingOff (); +#endif actor->SetNumberOfCloudPoints (int (std::max (1, data->GetNumberOfPoints () / 10))); actor->GetProperty ()->SetInterpolationToFlat (); @@ -1226,7 +1307,9 @@ pcl::visualization::PCLVisualizer::createActorFromVTKDataSet (const vtkSmartPoin mapper->ScalarVisibilityOn (); } } +#if VTK_RENDERING_BACKEND_OPENGL_VERSION < 2 mapper->ImmediateModeRenderingOff (); +#endif //actor->SetNumberOfCloudPoints (int (std::max (1, data->GetNumberOfPoints () / 10))); actor->GetProperty ()->SetInterpolationToFlat (); @@ -1432,6 +1515,46 @@ pcl::visualization::PCLVisualizer::getPointCloudRenderingProperties (int propert } return (true); } +///////////////////////////////////////////////////////////////////////////////////////////// +bool +pcl::visualization::PCLVisualizer::getPointCloudRenderingProperties (RenderingProperties property, + double &val1, + double &val2, + double &val3, + const std::string &id) +{ + // Check to see if this ID entry already exists (has it been already added to the visualizer?) + CloudActorMap::iterator am_it = cloud_actor_map_->find (id); + + if (am_it == cloud_actor_map_->end ()) + return (false); + // Get the actor pointer + vtkLODActor* actor = vtkLODActor::SafeDownCast (am_it->second.actor); + if (!actor) + return (false); + + switch (property) + { + case PCL_VISUALIZER_COLOR: + { + double rgb[3]; + actor->GetProperty ()->GetColor (rgb); + val1 = rgb[0]; + val2 = rgb[1]; + val3 = rgb[2]; + break; + } + default: + { + pcl::console::print_error ("[getPointCloudRenderingProperties] " + "Property (%d) is either unknown or it requires a different " + "number of variables to retrieve its contents.\n", + property); + return (false); + } + } + return (true); +} ///////////////////////////////////////////////////////////////////////////////////////////// bool @@ -1472,7 +1595,9 @@ pcl::visualization::PCLVisualizer::setPointCloudRenderingProperties ( // using immediate more rendering. case PCL_VISUALIZER_IMMEDIATE_RENDERING: { +#if VTK_RENDERING_BACKEND_OPENGL_VERSION < 2 actor->GetMapper ()->SetImmediateModeRendering (int (value)); +#endif actor->Modified (); break; } @@ -1557,7 +1682,7 @@ pcl::visualization::PCLVisualizer::setPointCloudSelected (const bool selected, c if (selected) { actor->GetProperty ()->EdgeVisibilityOn (); - actor->GetProperty ()->SetEdgeColor (1.0,0.0,0.0); + actor->GetProperty ()->SetEdgeColor (1.0, 0.0, 0.0); actor->Modified (); } else @@ -1616,6 +1741,58 @@ pcl::visualization::PCLVisualizer::setShapeRenderingProperties ( return (true); } +///////////////////////////////////////////////////////////////////////////////////////////// +bool +pcl::visualization::PCLVisualizer::setShapeRenderingProperties ( + int property, double val1, double val2, const std::string &id, int) +{ + // Check to see if this ID entry already exists (has it been already added to the visualizer?) + ShapeActorMap::iterator am_it = shape_actor_map_->find (id); + + if (am_it == shape_actor_map_->end ()) + { + pcl::console::print_error ("[setShapeRenderingProperties] Could not find any shape with id <%s>!\n", id.c_str ()); + return (false); + } + // Get the actor pointer + vtkActor* actor = vtkActor::SafeDownCast (am_it->second); + if (!actor) + return (false); + + switch (property) + { + case PCL_VISUALIZER_LUT_RANGE: + { + // Check if the mapper has scalars + if (!actor->GetMapper ()->GetInput ()->GetPointData ()->GetScalars ()) + break; + + // Check that scalars are not unisgned char (i.e. check if a LUT is used to colormap scalars assuming vtk ColorMode is Default) + if (actor->GetMapper ()->GetInput ()->GetPointData ()->GetScalars ()->IsA ("vtkUnsignedCharArray")) + break; + + // Check that range values are correct + if (val1 >= val2) + { + PCL_WARN ("[setShapeRenderingProperties] Range max must be greater than range min!\n"); + return (false); + } + + // Update LUT + actor->GetMapper ()->GetLookupTable ()->SetRange (val1, val2); + actor->GetMapper()->UseLookupTableScalarRangeOn (); + style_->updateLookUpTableDisplay (false); + break; + } + default: + { + pcl::console::print_error ("[setShapeRenderingProperties] Unknown property (%d) specified!\n", property); + return (false); + } + } + return (true); +} + ///////////////////////////////////////////////////////////////////////////////////////////// bool pcl::visualization::PCLVisualizer::setShapeRenderingProperties ( @@ -1740,17 +1917,45 @@ pcl::visualization::PCLVisualizer::setShapeRenderingProperties ( if (!actor->GetMapper ()->GetInput ()->GetPointData ()->GetScalars ()) break; - double range[2]; - actor->GetMapper ()->GetInput ()->GetPointData ()->GetScalars ()->GetRange (range); + // Check that scalars are not unisgned char (i.e. check if a LUT is used to colormap scalars assuming vtk ColorMode is Default) + if (actor->GetMapper ()->GetInput ()->GetPointData ()->GetScalars ()->IsA ("vtkUnsignedCharArray")) + break; + + // Get range limits from existing LUT + double *range; + range = actor->GetMapper ()->GetLookupTable ()->GetRange (); + actor->GetMapper ()->ScalarVisibilityOn (); actor->GetMapper ()->SetScalarRange (range[0], range[1]); - vtkSmartPointer table = vtkSmartPointer::New (); - getColormapLUT (static_cast(static_cast(value)), table); + vtkSmartPointer table; + if (!pcl::visualization::getColormapLUT (static_cast(static_cast(value)), table)) + break; table->SetRange (range[0], range[1]); actor->GetMapper ()->SetLookupTable (table); style_->updateLookUpTableDisplay (false); - break; } + case PCL_VISUALIZER_LUT_RANGE: + { + // Check if the mapper has scalars + if (!actor->GetMapper ()->GetInput ()->GetPointData ()->GetScalars ()) + break; + + // Check that scalars are not unisgned char (i.e. check if a LUT is used to colormap scalars assuming vtk ColorMode is Default) + if (actor->GetMapper ()->GetInput ()->GetPointData ()->GetScalars ()->IsA ("vtkUnsignedCharArray")) + break; + + switch (int(value)) + { + case PCL_VISUALIZER_LUT_RANGE_AUTO: + double range[2]; + actor->GetMapper ()->GetInput ()->GetPointData ()->GetScalars ()->GetRange (range); + actor->GetMapper ()->GetLookupTable ()->SetRange (range[0], range[1]); + actor->GetMapper ()->UseLookupTableScalarRangeOn (); + style_->updateLookUpTableDisplay (false); + break; + } + break; + } default: { pcl::console::print_error ("[setShapeRenderingProperties] Unknown property (%d) specified!\n", property); @@ -1906,7 +2111,7 @@ pcl::visualization::PCLVisualizer::getCameras (std::vectorGetNextItem ()) != NULL) { - cameras.push_back(Camera()); + cameras.push_back (Camera ()); cameras.back ().pos[0] = renderer->GetActiveCamera ()->GetPosition ()[0]; cameras.back ().pos[1] = renderer->GetActiveCamera ()->GetPosition ()[1]; cameras.back ().pos[2] = renderer->GetActiveCamera ()->GetPosition ()[2]; @@ -2267,7 +2472,7 @@ pcl::visualization::PCLVisualizer::addCube (float x_min, float x_max, vtkSmartPointer actor; createActorFromVTKDataSet (data, actor); actor->GetProperty ()->SetRepresentationToSurface (); - actor->GetProperty ()->SetColor (r,g,b); + actor->GetProperty ()->SetColor (r, g, b); addActorToRenderer (actor, viewport); // Save the pointer/ID pair to the global actor map @@ -2352,7 +2557,7 @@ pcl::visualization::PCLVisualizer::addModelFromPolyData ( #else trans_filter->SetInputData (polydata); #endif - trans_filter->Update(); + trans_filter->Update (); // Create an Actor vtkSmartPointer actor; @@ -2837,10 +3042,10 @@ pcl::visualization::PCLVisualizer::updateColorHandlerIndex (const std::string &i return (false); } - int color_handler_size = int (am_it->second.color_handlers.size ()); - if (index >= color_handler_size) + size_t color_handler_size = am_it->second.color_handlers.size (); + if (!(size_t (index) < color_handler_size)) { - pcl::console::print_warn (stderr, "[updateColorHandlerIndex] Invalid index <%d> given! Maximum range is: 0-%lu.\n", index, static_cast (am_it->second.color_handlers.size ())); + pcl::console::print_warn (stderr, "[updateColorHandlerIndex] Invalid index <%d> given! Index must be less than %d.\n", index, int (color_handler_size)); return (false); } // Get the handler @@ -2913,9 +3118,11 @@ pcl::visualization::PCLVisualizer::addPolygonMesh (const pcl::PolygonMesh &poly_ pcl::fromPCLPointCloud2 (poly_mesh.cloud, *point_cloud); poly_points->SetNumberOfPoints (point_cloud->points.size ()); - size_t i; - for (i = 0; i < point_cloud->points.size (); ++i) - poly_points->InsertPoint (i, point_cloud->points[i].x, point_cloud->points[i].y, point_cloud->points[i].z); + for (size_t i = 0; i < point_cloud->points.size (); ++i) + { + const pcl::PointXYZ& p = point_cloud->points[i]; + poly_points->InsertPoint (i, p.x, p.y, p.z); + } bool has_color = false; vtkSmartPointer colors = vtkSmartPointer::New (); @@ -2925,34 +3132,34 @@ pcl::visualization::PCLVisualizer::addPolygonMesh (const pcl::PolygonMesh &poly_ colors->SetNumberOfComponents (3); colors->SetName ("Colors"); pcl::PointCloud cloud; - pcl::fromPCLPointCloud2(poly_mesh.cloud, cloud); - for (i = 0; i < cloud.points.size (); ++i) + pcl::fromPCLPointCloud2 (poly_mesh.cloud, cloud); + for (size_t i = 0; i < cloud.points.size (); ++i) { - const unsigned char color[3] = {cloud.points[i].r, cloud.points[i].g, cloud.points[i].b}; - colors->InsertNextTupleValue(color); + const unsigned char color[3] = { cloud.points[i].r, cloud.points[i].g, cloud.points[i].b }; + colors->InsertNextTupleValue (color); } } - if (pcl::getFieldIndex(poly_mesh.cloud, "rgba") != -1) + if (pcl::getFieldIndex (poly_mesh.cloud, "rgba") != -1) { has_color = true; colors->SetNumberOfComponents (3); colors->SetName ("Colors"); pcl::PointCloud cloud; - pcl::fromPCLPointCloud2(poly_mesh.cloud, cloud); - for (i = 0; i < cloud.points.size (); ++i) + pcl::fromPCLPointCloud2 (poly_mesh.cloud, cloud); + for (size_t i = 0; i < cloud.points.size (); ++i) { - const unsigned char color[3] = {cloud.points[i].r, cloud.points[i].g, cloud.points[i].b}; - colors->InsertNextTupleValue(color); + const unsigned char color[3] = { cloud.points[i].r, cloud.points[i].g, cloud.points[i].b }; + colors->InsertNextTupleValue (color); } } vtkSmartPointer actor; - if (poly_mesh.polygons.size() > 1) + if (poly_mesh.polygons.size () > 1) { //create polys from polyMesh.polygons vtkSmartPointer cell_array = vtkSmartPointer::New (); - for (i = 0; i < poly_mesh.polygons.size (); i++) + for (size_t i = 0; i < poly_mesh.polygons.size (); i++) { size_t n_points (poly_mesh.polygons[i].vertices.size ()); cell_array->InsertNextCell (int (n_points)); @@ -2960,23 +3167,23 @@ pcl::visualization::PCLVisualizer::addPolygonMesh (const pcl::PolygonMesh &poly_ cell_array->InsertCellPoint (poly_mesh.polygons[i].vertices[j]); } - vtkSmartPointer polydata = vtkSmartPointer::New(); -// polydata->SetStrips (cell_array); + vtkSmartPointer polydata = vtkSmartPointer::New (); + // polydata->SetStrips (cell_array); polydata->SetPolys (cell_array); polydata->SetPoints (poly_points); if (has_color) - polydata->GetPointData()->SetScalars(colors); + polydata->GetPointData ()->SetScalars (colors); createActorFromVTKDataSet (polydata, actor); } - else if (poly_mesh.polygons.size() == 1) + else if (poly_mesh.polygons.size () == 1) { vtkSmartPointer polygon = vtkSmartPointer::New (); size_t n_points = poly_mesh.polygons[0].vertices.size (); polygon->GetPointIds ()->SetNumberOfIds (n_points - 1); - for (size_t j=0; j < (n_points - 1); j++) + for (size_t j = 0; j < (n_points - 1); j++) polygon->GetPointIds ()->SetId (j, poly_mesh.polygons[0].vertices[j]); vtkSmartPointer poly_grid = vtkSmartPointer::New (); @@ -2989,7 +3196,7 @@ pcl::visualization::PCLVisualizer::addPolygonMesh (const pcl::PolygonMesh &poly_ } else { - PCL_ERROR("PCLVisualizer::addPolygonMesh: No polygons\n"); + PCL_ERROR ("PCLVisualizer::addPolygonMesh: No polygons\n"); return false; } @@ -3014,7 +3221,7 @@ pcl::visualization::PCLVisualizer::updatePolygonMesh ( const std::string &id) { - if (poly_mesh.polygons.empty()) + if (poly_mesh.polygons.empty ()) { pcl::console::print_error ("[updatePolygonMesh] No vertices given!\n"); return (false); @@ -3029,7 +3236,7 @@ pcl::visualization::PCLVisualizer::updatePolygonMesh ( pcl::PointCloud::Ptr cloud (new pcl::PointCloud ()); pcl::fromPCLPointCloud2 (poly_mesh.cloud, *cloud); - std::vector verts(poly_mesh.polygons); // copy vector + std::vector verts (poly_mesh.polygons); // copy vector // Get the current poly data vtkSmartPointer polydata = static_cast(am_it->second.actor->GetMapper ())->GetInput (); @@ -3038,7 +3245,7 @@ pcl::visualization::PCLVisualizer::updatePolygonMesh ( vtkSmartPointer cells = polydata->GetStrips (); if (!cells) return (false); - vtkSmartPointer points = polydata->GetPoints (); + vtkSmartPointer points = polydata->GetPoints (); // Copy the new point array in vtkIdType nr_points = cloud->points.size (); points->SetNumberOfPoints (nr_points); @@ -3052,7 +3259,7 @@ pcl::visualization::PCLVisualizer::updatePolygonMesh ( if (cloud->is_dense) { for (vtkIdType i = 0; i < nr_points; ++i, ptr += 3) - memcpy (&data[ptr], &cloud->points[i].x, sizeof (float) * 3); + std::copy (&cloud->points[i].x, &cloud->points[i].x + 3, &data[ptr]); } else { @@ -3064,8 +3271,8 @@ pcl::visualization::PCLVisualizer::updatePolygonMesh ( if (!isFinite (cloud->points[i])) continue; - lookup [i] = static_cast (j); - memcpy (&data[ptr], &cloud->points[i].x, sizeof (float) * 3); + lookup[i] = static_cast (j); + std::copy (&cloud->points[i].x, &cloud->points[i].x + 3, &data[ptr]); j++; ptr += 3; } @@ -3147,7 +3354,7 @@ pcl::visualization::PCLVisualizer::addPolylineFromPolygonMesh ( polyLine->GetPointIds()->SetNumberOfIds(polymesh.polygons[i].vertices.size()); for(unsigned int k = 0; k < polymesh.polygons[i].vertices.size(); k++) { - polyLine->GetPointIds()->SetId(k,polymesh.polygons[i].vertices[k]); + polyLine->GetPointIds ()->SetId (k, polymesh.polygons[i].vertices[k]); } cells->InsertNextCell (polyLine); @@ -3216,21 +3423,21 @@ pcl::visualization::PCLVisualizer::addTextureMesh (const pcl::TextureMesh &mesh, // total number of vertices std::size_t nb_vertices = 0; for (std::size_t i = 0; i < mesh.tex_polygons.size (); ++i) - nb_vertices+= mesh.tex_polygons[i].size (); + nb_vertices += mesh.tex_polygons[i].size (); // no vertices --> exit if (nb_vertices == 0) { - PCL_ERROR("[PCLVisualizer::addTextureMesh] No vertices found!\n"); + PCL_ERROR ("[PCLVisualizer::addTextureMesh] No vertices found!\n"); return (false); } // total number of coordinates std::size_t nb_coordinates = 0; for (std::size_t i = 0; i < mesh.tex_coordinates.size (); ++i) - nb_coordinates+= mesh.tex_coordinates[i].size (); + nb_coordinates += mesh.tex_coordinates[i].size (); // no texture coordinates --> exit if (nb_coordinates == 0) { - PCL_ERROR("[PCLVisualizer::addTextureMesh] No textures coordinates found!\n"); + PCL_ERROR ("[PCLVisualizer::addTextureMesh] No textures coordinates found!\n"); return (false); } @@ -3243,10 +3450,10 @@ pcl::visualization::PCLVisualizer::addTextureMesh (const pcl::TextureMesh &mesh, (pcl::getFieldIndex(mesh.cloud, "rgb") != -1)) { pcl::PointCloud cloud; - pcl::fromPCLPointCloud2(mesh.cloud, cloud); + pcl::fromPCLPointCloud2 (mesh.cloud, cloud); if (cloud.points.size () == 0) { - PCL_ERROR("[PCLVisualizer::addTextureMesh] Cloud is empty!\n"); + PCL_ERROR ("[PCLVisualizer::addTextureMesh] Cloud is empty!\n"); return (false); } convertToVtkMatrix (cloud.sensor_origin_, cloud.sensor_orientation_, transformation); @@ -3258,8 +3465,8 @@ pcl::visualization::PCLVisualizer::addTextureMesh (const pcl::TextureMesh &mesh, { const pcl::PointXYZRGB &p = cloud.points[i]; poly_points->InsertPoint (i, p.x, p.y, p.z); - const unsigned char color[3] = {p.r, p.g, p.b}; - colors->InsertNextTupleValue(color); + const unsigned char color[3] = { p.r, p.g, p.b }; + colors->InsertNextTupleValue (color); } } else @@ -3269,7 +3476,7 @@ pcl::visualization::PCLVisualizer::addTextureMesh (const pcl::TextureMesh &mesh, // no points --> exit if (cloud->points.size () == 0) { - PCL_ERROR("[PCLVisualizer::addTextureMesh] Cloud is empty!\n"); + PCL_ERROR ("[PCLVisualizer::addTextureMesh] Cloud is empty!\n"); return (false); } convertToVtkMatrix (cloud->sensor_origin_, cloud->sensor_orientation_, transformation); @@ -3294,11 +3501,11 @@ pcl::visualization::PCLVisualizer::addTextureMesh (const pcl::TextureMesh &mesh, } } - vtkSmartPointer polydata = vtkSmartPointer::New(); + vtkSmartPointer polydata = vtkSmartPointer::New (); polydata->SetPolys (polys); polydata->SetPoints (poly_points); if (has_color) - polydata->GetPointData()->SetScalars(colors); + polydata->GetPointData ()->SetScalars (colors); vtkSmartPointer mapper = vtkSmartPointer::New (); #if VTK_MAJOR_VERSION < 6 @@ -3311,82 +3518,57 @@ pcl::visualization::PCLVisualizer::addTextureMesh (const pcl::TextureMesh &mesh, vtkTextureUnitManager* tex_manager = vtkOpenGLRenderWindow::SafeDownCast (win_)->GetTextureUnitManager (); if (!tex_manager) return (false); - // Check if hardware support multi texture + // hardware always supports multitexturing of some degree int texture_units = tex_manager->GetNumberOfTextureUnits (); - if ((mesh.tex_materials.size () > 1) && (texture_units > 1)) - { - if (texture_units < mesh.tex_materials.size ()) - PCL_WARN ("[PCLVisualizer::addTextureMesh] GPU texture units %d < mesh textures %d!\n", - texture_units, mesh.tex_materials.size ()); - // Load textures - std::size_t last_tex_id = std::min (static_cast (mesh.tex_materials.size ()), texture_units); - int tu = vtkProperty::VTK_TEXTURE_UNIT_0; - std::size_t tex_id = 0; - while (tex_id < last_tex_id) - { - vtkSmartPointer texture = vtkSmartPointer::New (); - if (textureFromTexMaterial (mesh.tex_materials[tex_id], texture)) - { - PCL_WARN ("[PCLVisualizer::addTextureMesh] Failed to load texture %s, skipping!\n", - mesh.tex_materials[tex_id].tex_name.c_str ()); - continue; - } - // the first texture is in REPLACE mode others are in ADD mode - if (tex_id == 0) - texture->SetBlendingMode(vtkTexture::VTK_TEXTURE_BLENDING_MODE_REPLACE); - else - texture->SetBlendingMode(vtkTexture::VTK_TEXTURE_BLENDING_MODE_ADD); - // add a texture coordinates array per texture - vtkSmartPointer coordinates = vtkSmartPointer::New (); - coordinates->SetNumberOfComponents (2); - std::stringstream ss; ss << "TCoords" << tex_id; - std::string this_coordinates_name = ss.str (); - coordinates->SetName (this_coordinates_name.c_str ()); - - for (std::size_t t = 0 ; t < mesh.tex_coordinates.size (); ++t) - if (t == tex_id) - for (std::size_t tc = 0; tc < mesh.tex_coordinates[t].size (); ++tc) - coordinates->InsertNextTuple2 (mesh.tex_coordinates[t][tc][0], - mesh.tex_coordinates[t][tc][1]); - else - for (std::size_t tc = 0; tc < mesh.tex_coordinates[t].size (); ++tc) - coordinates->InsertNextTuple2 (-1.0, -1.0); - - mapper->MapDataArrayToMultiTextureAttribute(tu, - this_coordinates_name.c_str (), - vtkDataObject::FIELD_ASSOCIATION_POINTS); - polydata->GetPointData ()->AddArray (coordinates); - actor->GetProperty ()->SetTexture(tu, texture); - ++tex_id; - ++tu; - } - } // end of multi texturing - else - { - if ((mesh.tex_materials.size () > 1) && (texture_units < 2)) - PCL_WARN ("[PCLVisualizer::addTextureMesh] Your GPU doesn't support multi texturing. " - "Will use first one only!\n"); + if ((size_t) texture_units < mesh.tex_materials.size ()) + PCL_WARN ("[PCLVisualizer::addTextureMesh] GPU texture units %d < mesh textures %d!\n", + texture_units, mesh.tex_materials.size ()); + // Load textures + std::size_t last_tex_id = std::min (static_cast (mesh.tex_materials.size ()), texture_units); + std::size_t tex_id = 0; + while (tex_id < last_tex_id) + { +#if VTK_MAJOR_VERSION < 9 + int tu = vtkProperty::VTK_TEXTURE_UNIT_0 + tex_id; +#else + const char *tu = mesh.tex_materials[tex_id].tex_name.c_str (); +#endif vtkSmartPointer texture = vtkSmartPointer::New (); - // fill vtkTexture from pcl::TexMaterial structure - if (textureFromTexMaterial (mesh.tex_materials[0], texture)) - PCL_WARN ("[PCLVisualizer::addTextureMesh] Failed to create vtkTexture from %s!\n", - mesh.tex_materials[0].tex_name.c_str ()); - - // set texture coordinates - vtkSmartPointer coordinates = vtkSmartPointer::New (); - coordinates->SetNumberOfComponents (2); - coordinates->SetNumberOfTuples (mesh.tex_coordinates[0].size ()); - for (std::size_t tc = 0; tc < mesh.tex_coordinates[0].size (); ++tc) + if (textureFromTexMaterial (mesh.tex_materials[tex_id], texture)) { - const Eigen::Vector2f &uv = mesh.tex_coordinates[0][tc]; - coordinates->SetTuple2 (tc, uv[0], uv[1]); + PCL_WARN ("[PCLVisualizer::addTextureMesh] Failed to load texture %s, skipping!\n", + mesh.tex_materials[tex_id].tex_name.c_str ()); + continue; } - coordinates->SetName ("TCoords"); - polydata->GetPointData ()->SetTCoords(coordinates); - // apply texture - actor->SetTexture (texture); - } // end of one texture + // the first texture is in REPLACE mode others are in ADD mode + if (tex_id == 0) + texture->SetBlendingMode(vtkTexture::VTK_TEXTURE_BLENDING_MODE_REPLACE); + else + texture->SetBlendingMode(vtkTexture::VTK_TEXTURE_BLENDING_MODE_ADD); + // add a texture coordinates array per texture + vtkSmartPointer coordinates = vtkSmartPointer::New (); + coordinates->SetNumberOfComponents (2); + std::stringstream ss; ss << "TCoords" << tex_id; + std::string this_coordinates_name = ss.str (); + coordinates->SetName (this_coordinates_name.c_str ()); + + for (std::size_t t = 0; t < mesh.tex_coordinates.size (); ++t) + if (t == tex_id) + for (std::size_t tc = 0; tc < mesh.tex_coordinates[t].size (); ++tc) + coordinates->InsertNextTuple2 (mesh.tex_coordinates[t][tc][0], + mesh.tex_coordinates[t][tc][1]); + else + for (std::size_t tc = 0; tc < mesh.tex_coordinates[t].size (); ++tc) + coordinates->InsertNextTuple2 (-1.0, -1.0); + + mapper->MapDataArrayToMultiTextureAttribute(tu, + this_coordinates_name.c_str (), + vtkDataObject::FIELD_ASSOCIATION_POINTS); + polydata->GetPointData ()->AddArray (coordinates); + actor->GetProperty ()->SetTexture (tu, texture); + ++tex_id; + } // set mapper actor->SetMapper (mapper); @@ -3416,7 +3598,7 @@ pcl::visualization::PCLVisualizer::setRepresentationToSurfaceForAllActors () while ((actor = actors->GetNextActor ()) != NULL) { actor->GetProperty ()->SetRepresentationToSurface (); - actor->GetProperty ()->SetLighting(true); + actor->GetProperty ()->SetLighting (true); } } } @@ -3455,7 +3637,7 @@ pcl::visualization::PCLVisualizer::setRepresentationToWireframeForAllActors () while ((actor = actors->GetNextActor ()) != NULL) { actor->GetProperty ()->SetRepresentationToWireframe (); - actor->GetProperty ()->SetLighting(false); + actor->GetProperty ()->SetLighting (false); } } } @@ -3469,6 +3651,14 @@ pcl::visualization::PCLVisualizer::setShowFPS (bool show_fps) } +/////////////////////////////////////////////////////////////////////////////////// +float +pcl::visualization::PCLVisualizer::getFPS () const +{ + return (update_fps_->last_fps); +} + + /////////////////////////////////////////////////////////////////////////////////// void pcl::visualization::PCLVisualizer::renderViewTesselatedSphere ( @@ -3582,7 +3772,7 @@ pcl::visualization::PCLVisualizer::renderViewTesselatedSphere ( ico->SetSolidTypeToIcosahedron (); ico->Update (); - //tesselate cells from icosahedron + //tessellate cells from icosahedron vtkSmartPointer subdivide = vtkSmartPointer::New (); subdivide->SetNumberOfSubdivisions (tesselation_level); subdivide->SetInputConnection (ico->GetOutputPort ()); @@ -3597,7 +3787,7 @@ pcl::visualization::PCLVisualizer::renderViewTesselatedSphere ( vtkSmartPointer cells_sphere = sphere->GetPolys (); cam_positions.resize (sphere->GetNumberOfPolys ()); - size_t i=0; + size_t i = 0; for (cells_sphere->InitTraversal (); cells_sphere->GetNextCell (npts_com, ptIds_com);) { sphere->GetPoint (ptIds_com[0], p1_com); @@ -3651,7 +3841,7 @@ pcl::visualization::PCLVisualizer::renderViewTesselatedSphere ( cam->SetViewAngle (view_angle); cam->Modified (); - //For each camera position, traposesnsform the object and render view + //For each camera position, transform the object and render view for (size_t i = 0; i < cam_positions.size (); i++) { cam_pos[0] = cam_positions[i][0]; @@ -3833,43 +4023,43 @@ pcl::visualization::PCLVisualizer::renderViewTesselatedSphere ( #else //THIS CAN BE USED WHEN VTK >= 5.4 IS REQUIRED... vtkVisibleCellSelector is deprecated from VTK5.4 vtkSmartPointer hardware_selector = vtkSmartPointer::New (); - hardware_selector->ClearBuffers(); - vtkSmartPointer hdw_selection = vtkSmartPointer::New (); - hardware_selector->SetRenderer (renderer); - hardware_selector->SetArea (0, 0, xres - 1, yres - 1); - hardware_selector->SetFieldAssociation(vtkDataObject::FIELD_ASSOCIATION_CELLS); - hdw_selection = hardware_selector->Select (); - if (!hdw_selection || !hdw_selection->GetNode (0) || !hdw_selection->GetNode (0)->GetSelectionList ()) - { - PCL_WARN ("[renderViewTesselatedSphere] Invalid selection, skipping!\n"); - continue; - } - - vtkSmartPointer ids; - ids = vtkIdTypeArray::SafeDownCast(hdw_selection->GetNode(0)->GetSelectionList()); - if (!ids) - return; - double visible_area = 0; - for (int sel_id = 0; sel_id < (ids->GetNumberOfTuples ()); sel_id++) - { - int id_mesh = static_cast (ids->GetValue (sel_id)); - vtkCell * cell = polydata->GetCell (id_mesh); - vtkTriangle* triangle = dynamic_cast (cell); - if (!triangle) - { - PCL_WARN ("[renderViewTesselatedSphere] Invalid triangle %d, skipping!\n", id_mesh); - continue; - } - - double p0[3]; - double p1[3]; - double p2[3]; - triangle->GetPoints ()->GetPoint (0, p0); - triangle->GetPoints ()->GetPoint (1, p1); - triangle->GetPoints ()->GetPoint (2, p2); - area = vtkTriangle::TriangleArea (p0, p1, p2); - visible_area += area; - } + hardware_selector->ClearBuffers (); + vtkSmartPointer hdw_selection = vtkSmartPointer::New (); + hardware_selector->SetRenderer (renderer); + hardware_selector->SetArea (0, 0, xres - 1, yres - 1); + hardware_selector->SetFieldAssociation (vtkDataObject::FIELD_ASSOCIATION_CELLS); + hdw_selection = hardware_selector->Select (); + if (!hdw_selection || !hdw_selection->GetNode (0) || !hdw_selection->GetNode (0)->GetSelectionList ()) + { + PCL_WARN ("[renderViewTesselatedSphere] Invalid selection, skipping!\n"); + continue; + } + + vtkSmartPointer ids; + ids = vtkIdTypeArray::SafeDownCast (hdw_selection->GetNode (0)->GetSelectionList ()); + if (!ids) + return; + double visible_area = 0; + for (int sel_id = 0; sel_id < (ids->GetNumberOfTuples ()); sel_id++) + { + int id_mesh = static_cast (ids->GetValue (sel_id)); + vtkCell * cell = polydata->GetCell (id_mesh); + vtkTriangle* triangle = dynamic_cast (cell); + if (!triangle) + { + PCL_WARN ("[renderViewTesselatedSphere] Invalid triangle %d, skipping!\n", id_mesh); + continue; + } + + double p0[3]; + double p1[3]; + double p2[3]; + triangle->GetPoints ()->GetPoint (0, p0); + triangle->GetPoints ()->GetPoint (1, p1); + triangle->GetPoints ()->GetPoint (2, p2); + area = vtkTriangle::TriangleArea (p0, p1, p2); + visible_area += area; + } #endif enthropies.push_back (static_cast (visible_area / totalArea)); @@ -3937,7 +4127,7 @@ pcl::visualization::PCLVisualizer::renderView (int xres, int yres, pcl::PointClo { if (rens_->GetNumberOfItems () > 1) { - PCL_WARN("[renderView] Method will render only the first viewport\n"); + PCL_WARN ("[renderView] Method will render only the first viewport\n"); return; } @@ -4100,7 +4290,7 @@ pcl::visualization::PCLVisualizer::updateCells (vtkSmartPointer for (vtkIdType i = 0; i < nr_points; ++i, cell += 2) { *cell = 1; - *(cell+1) = i; + *(cell + 1) = i; } // Save the results in initcells @@ -4143,8 +4333,8 @@ pcl::visualization::PCLVisualizer::getTransformationMatrix ( Eigen::Matrix4f &transformation) { transformation.setIdentity (); - transformation.block<3,3>(0,0) = orientation.toRotationMatrix (); - transformation.block<3,1>(0,3) = origin.head (3); + transformation.block<3, 3> (0, 0) = orientation.toRotationMatrix (); + transformation.block<3, 1> (0, 3) = origin.head (3); } ////////////////////////////////////////////////////////////////////////////////////////////// @@ -4186,7 +4376,7 @@ pcl::visualization::PCLVisualizer::convertToEigenMatrix ( { for (int i = 0; i < 4; i++) for (int k = 0; k < 4; k++) - m (i,k) = static_cast (vtk_matrix->GetElement (i, k)); + m (i, k) = static_cast (vtk_matrix->GetElement (i, k)); } ////////////////////////////////////////////////////////////////////////////////////////////// @@ -4282,7 +4472,7 @@ pcl::visualization::PCLVisualizer::setWindowBorders (bool mode) if (win_) win_->SetBorders (mode); } - + ////////////////////////////////////////////////////////////////////////////////////////////// void pcl::visualization::PCLVisualizer::setPosition (int x, int y) @@ -4293,7 +4483,7 @@ pcl::visualization::PCLVisualizer::setPosition (int x, int y) win_->Render (); } } - + ////////////////////////////////////////////////////////////////////////////////////////////// void pcl::visualization::PCLVisualizer::setSize (int xw, int yw) @@ -4310,14 +4500,18 @@ void pcl::visualization::PCLVisualizer::close () { #if ((VTK_MAJOR_VERSION == 5) && (VTK_MINOR_VERSION <= 4)) - interactor_->stopped = true; - // This tends to close the window... - interactor_->stopLoop (); + if (interactor_) + { + interactor_->stopped = true; + // This tends to close the window... + interactor_->stopLoop (); + } #else stopped_ = true; // This tends to close the window... win_->Finalize (); - interactor_->TerminateApp (); + if (interactor_) + interactor_->TerminateApp (); #endif } @@ -4355,13 +4549,13 @@ pcl::visualization::PCLVisualizer::getGeometryHandlerIndex (const std::string &i bool pcl::visualization::PCLVisualizer::wasStopped () const { - if (interactor_ != NULL) + if (interactor_ != NULL) #if ((VTK_MAJOR_VERSION == 5) && (VTK_MINOR_VERSION <= 4)) return (interactor_->stopped); #else - return (stopped_); + return (stopped_); #endif - else + else return (true); } @@ -4369,11 +4563,11 @@ pcl::visualization::PCLVisualizer::wasStopped () const void pcl::visualization::PCLVisualizer::resetStoppedFlag () { - if (interactor_ != NULL) + if (interactor_ != NULL) #if ((VTK_MAJOR_VERSION == 5) && (VTK_MINOR_VERSION <= 4)) interactor_->stopped = false; #else - stopped_ = false; + stopped_ = false; #endif } @@ -4404,7 +4598,7 @@ pcl::visualization::PCLVisualizer::ExitMainLoopTimerCallback::Execute ( { if (event_id != vtkCommand::TimerEvent) return; - int timer_id = * static_cast (call_data); + int timer_id = *static_cast (call_data); //PCL_WARN ("[pcl::visualization::PCLVisualizer::ExitMainLoopTimerCallback] Timer %d called.\n", timer_id); if (timer_id != right_timer_id) return; @@ -4415,7 +4609,7 @@ pcl::visualization::PCLVisualizer::ExitMainLoopTimerCallback::Execute ( pcl_visualizer->interactor_->TerminateApp (); #endif } - + ////////////////////////////////////////////////////////////////////////////////////////////// void pcl::visualization::PCLVisualizer::ExitCallback::Execute ( @@ -4424,13 +4618,17 @@ pcl::visualization::PCLVisualizer::ExitCallback::Execute ( if (event_id != vtkCommand::ExitEvent) return; #if ((VTK_MAJOR_VERSION == 5) && (VTK_MINOR_VERSION <= 4)) - pcl_visualizer->interactor_->stopped = true; - // This tends to close the window... - pcl_visualizer->interactor_->stopLoop (); + if (pcl_visualizer->interactor_) + { + pcl_visualizer->interactor_->stopped = true; + // This tends to close the window... + pcl_visualizer->interactor_->stopLoop (); + } #else pcl_visualizer->stopped_ = true; // This tends to close the window... - pcl_visualizer->interactor_->TerminateApp (); + if (pcl_visualizer->interactor_) + pcl_visualizer->interactor_->TerminateApp (); #endif } @@ -4442,9 +4640,9 @@ pcl::visualization::PCLVisualizer::FPSCallback::Execute ( vtkObject* caller, unsigned long, void*) { vtkRenderer *ren = reinterpret_cast (caller); - float fps = 1.0f / static_cast (ren->GetLastRenderTimeInSeconds ()); + last_fps = 1.0f / static_cast (ren->GetLastRenderTimeInSeconds ()); char buf[128]; - sprintf (buf, "%.1f FPS", fps); + sprintf (buf, "%.1f FPS", last_fps); actor->SetInput (buf); } diff --git a/visualization/test/CMakeLists.txt b/visualization/test/CMakeLists.txt index 1e5d1a42..43cf1dbd 100644 --- a/visualization/test/CMakeLists.txt +++ b/visualization/test/CMakeLists.txt @@ -6,6 +6,14 @@ #target_link_libraries(test_geometry pcl_common pcl_features pcl_filters pcl_io pcl_kdtree pcl_visualization) #add_test(vis_test_geometry test_geometry) -#add_executable(demo_shapes test_shapes.cpp) -#target_link_libraries(demo_shapes pcl_common pcl_io pcl_kdtree pcl_visualization) +add_executable(demo_shapes test_shapes.cpp) +target_link_libraries(demo_shapes pcl_common pcl_io pcl_kdtree pcl_visualization) +add_executable(demo_shapes_multiport test_shapes_multiport.cpp) +target_link_libraries(demo_shapes_multiport pcl_common pcl_io pcl_kdtree pcl_visualization) + +add_executable(demo_text_simple text_simple.cpp) +target_link_libraries(demo_text_simple pcl_common pcl_visualization) + +add_executable(demo_text_simple_multiport text_simple_multiport.cpp) +target_link_libraries(demo_text_simple_multiport pcl_common pcl_visualization) diff --git a/visualization/test/test_shapes_multiport.cpp b/visualization/test/test_shapes_multiport.cpp new file mode 100644 index 00000000..82f471ed --- /dev/null +++ b/visualization/test/test_shapes_multiport.cpp @@ -0,0 +1,56 @@ +#include +#include + +using pcl::PointCloud; +using pcl::PointXYZ; + +int +main (int , char **) +{ + srand (unsigned (time (0))); + + PointCloud::Ptr cloud (new PointCloud); + + cloud->points.resize (5); + for (size_t i = 0; i < cloud->points.size (); ++i) + { + cloud->points[i].x = float (i); + cloud->points[i].y = float (i / 2); + cloud->points[i].z = 0.0f; + } + + // Start the visualizer + pcl::visualization::PCLVisualizer p ("test_shapes"); + int leftPort(0); + int rightPort(0); + p.createViewPort(0, 0, 0.5, 1, leftPort); + p.createViewPort(0.5, 0, 1, 1, rightPort); + p.setBackgroundColor (1, 1, 1); + p.addCoordinateSystem (1.0, "first"); + + //p.addPolygon (cloud, "polygon"); + p.addPolygon (cloud, 1.0, 0.0, 0.0, "polygon", leftPort); + p.setShapeRenderingProperties (pcl::visualization::PCL_VISUALIZER_LINE_WIDTH, 10, "polygon", leftPort); + + p.addLine (cloud->points[0], cloud->points[1], 0.0, 1.0, 0.0, "line", leftPort); + p.setShapeRenderingProperties (pcl::visualization::PCL_VISUALIZER_LINE_WIDTH, 50, "line", leftPort); + + p.addSphere (cloud->points[0], 1, 0.0, 1.0, 0.0, "sphere", leftPort); + p.setShapeRenderingProperties (pcl::visualization::PCL_VISUALIZER_LINE_WIDTH, 5, "sphere", leftPort); +// p.removePolygon ("poly"); + + p.addText ("text", 200, 200, 1.0, 0, 0, "text", leftPort); + + p.addText3D ("text3D", cloud->points[0], 1.0, 1.0, 0.0, 0.0, "", rightPort); + p.spin (); + p.removeCoordinateSystem ("first", 0); + p.spin (); + p.addCoordinateSystem (1.0, 5, 3, 1, "second"); + p.spin (); + p.removeCoordinateSystem ("second", 0); + p.spin (); + p.addText3D ("text3D_to_remove", cloud->points[1], 1.0, 0.0, 1.0, 0.0, "", rightPort); + p.spin (); + p.removeText3D ("text3D_to_remove", rightPort); + p.spin (); +} diff --git a/visualization/test/text_simple.cpp b/visualization/test/text_simple.cpp new file mode 100644 index 00000000..9b6c45e8 --- /dev/null +++ b/visualization/test/text_simple.cpp @@ -0,0 +1,24 @@ +#include + +#include + +int +main (int argc, char** argv) +{ + pcl::visualization::PCLVisualizer viz ("Visualizator"); + viz.addCoordinateSystem (1.0); + + viz.addText3D ("Following text", pcl::PointXYZ(0.0, 0.0, 0.0), + 1.0, 1.0, 0.0, 0.0, "id_following"); + viz.spin (); + double orientation[3] = {0., 0., 0.}; + viz.addText3D ("Fixed text", pcl::PointXYZ(0.0, 0.0, 0.0), orientation, + 1.0, 0.0, 1.0, 0.0, "id_fixed"); + viz.spin (); + viz.removeText3D ("id_following"); + viz.spin (); + viz.removeText3D ("id_fixed"); + viz.spin (); + + return (0); +} diff --git a/visualization/test/text_simple_multiport.cpp b/visualization/test/text_simple_multiport.cpp new file mode 100644 index 00000000..ebffcd62 --- /dev/null +++ b/visualization/test/text_simple_multiport.cpp @@ -0,0 +1,30 @@ +#include + +#include + +int +main (int argc, char** argv) +{ + pcl::visualization::PCLVisualizer viz ("Visualizator"); + int leftPort (0); + int rightPort (0); + + viz.createViewPort (0, 0, 0.5, 1, leftPort); + viz.createViewPort (0.5, 0, 1, 1, rightPort); + + viz.addCoordinateSystem (1.0); + + viz.addText3D ("Following text", pcl::PointXYZ(0.0, 0.0, 0.0), + 1.0, 1.0, 0.0, 0.0, "id_following", leftPort); + viz.spin (); + double orientation[3] = {0., 0., 0.}; + viz.addText3D ("Fixed text", pcl::PointXYZ(0.0, 0.0, 0.0), orientation, + 1.0, 0.0, 1.0, 0.0, "id_fixed", rightPort); + viz.spin (); + viz.removeText3D ("id_following", leftPort); + viz.spin (); + viz.removeText3D ("id_fixed", rightPort); + viz.spin (); + + return (0); +} diff --git a/visualization/tools/openni_image.cpp b/visualization/tools/openni_image.cpp index 7607f467..861ed72d 100644 --- a/visualization/tools/openni_image.cpp +++ b/visualization/tools/openni_image.cpp @@ -330,7 +330,7 @@ class Writer { { boost::mutex::scoped_lock io_lock (io_mutex); - print_info ("Writing remaing %ld clouds in the buffer to disk...\n", buf_.getSize ()); + print_info ("Writing remaining %ld clouds in the buffer to disk...\n", buf_.getSize ()); } while (!buf_.isEmpty ()) { diff --git a/visualization/tools/pcd_viewer.cpp b/visualization/tools/pcd_viewer.cpp index b9d2d5e7..f0a9e5a4 100644 --- a/visualization/tools/pcd_viewer.cpp +++ b/visualization/tools/pcd_viewer.cpp @@ -617,22 +617,26 @@ main (int argc, char** argv) { int rgb_idx = 0; int label_idx = 0; + int invalid_fields_count = 0; for (size_t f = 0; f < cloud->fields.size (); ++f) { + if (!isValidFieldName (cloud->fields[f].name)) + { + ++invalid_fields_count; + continue; + } if (cloud->fields[f].name == "rgb" || cloud->fields[f].name == "rgba") { - rgb_idx = f + 1; + rgb_idx = f - invalid_fields_count + 1 /* first is ColorHandlerRandom */; color_handler.reset (new pcl::visualization::PointCloudColorHandlerRGBField (cloud)); } else if (cloud->fields[f].name == "label") { - label_idx = f + 1; + label_idx = f - invalid_fields_count + 1; color_handler.reset (new pcl::visualization::PointCloudColorHandlerLabelField (cloud, !use_optimal_l_colors)); } else { - if (!isValidFieldName (cloud->fields[f].name)) - continue; color_handler.reset (new pcl::visualization::PointCloudColorHandlerGenericField (cloud, cloud->fields[f].name)); } // Add the cloud to the renderer -- 2.30.2